Сначала я скажу, что этот код выглядит очень Java-esque: объекты, которые являются «творцами вещей» (контроллер, который контролирует, профиль, который профилирует) - почему бы не просто контролировать и профилировать, когда это необходимо?Это может исключить необходимость в фабриках.
Но игнорируя эту точку и предполагая, что вам действительно нужны эти точки:
Ваши фабрики возвращают unique_ptr
с пользовательскими удалителями
Как предполагают комментаторы, ваши фабрики ведут себя странно.Они кажутся возвращающими значения типа okapi::ChassisControllerIntegrated
и okapi::AsyncMotionProfileController
соответственно (или типы, конвертируемые в эти два) - как только вы берете их адреса.Но это означает, что фабрики всегда возвращают один и тот же тип, что лишает цель иметь фабрику в первую очередь (фабрика может возвращать значение любого типа в некоторой иерархии через указатель на базовый класс).Если бы это было так, то, как сказал @me ', ваши созданные объекты были бы уничтожены при выходе из области видимости конструктора.
Если бы ваши фабрики возвращали указатели на эти два класса, код работал бы,но это была бы плохая идея, поскольку вам нужно было бы правильно отменить выделение двух указанных объектов при уничтожении (или даже отправить их на фабрики для уничтожения).
@ BobBills предлагает один из способовизбежать того, что оборачивает два созданных указателя в std::unique_ptr
.Это работает нормально, но только если вы можете наивно их освободить.
Я предлагаю вам сделать так, чтобы сами фабрики возвращали std::unique_ptr
с, с конкретнымиФункция удаления они нуждаются в вас, чтобы использовать.То, что вам действительно не придется беспокоиться об удалении вообще, равно как и любой другой код, использующий фабрики.
Код конструктора будет:
DriveController::DriveController(
int8_t portTL_, int8_t portTR_, int8_t portBL_, int8_t portBR_,
double wheelSize, double baseSize)
:
portTL{ portTL_}, portTR{ portTR_},
portBL{ portBL_}, portBR{ portBR_},
chassisController {
okapi::ChassisControllerFactory::create(
{portTL, portBL}, // Left motors
{portTR, portBR}, // Right motors
okapi::AbstractMotor::gearset::red, // torque gearset
{wheelSize, baseSize} // wheel radius, base width
)
},
chassisMotionProfiler {
okapi::AsyncControllerFactory::motionProfile(
1.0, 2.0, 10.0, chassisController)
}
{ }
(так же, как с @Решение BobBills) - и преимущество в том, что деструктор можно смело считать тривиальным:
DriveController::~DriveController() = default;
Рассмотрим альтернативы без указателей
Если ваш код DeviceController
может знатьзаранее все различные типы контроллеров шасси и контроллеров профилей, вы действительно можете заставить ваше фабрику возвращать значение - std::variant
, которое может содержать одно значение любого из нескольких фиксированных типов, например std::variant<int, double>
может содержать int
или double
, но не оба;и он занимает память, которая немного больше максимальной памяти для разных типов.Таким образом, вы можете полностью избежать указателей, и DeviceController
будет иметь элементы без указателей для контроллеров шасси и профилей.
Другой способ избежать использования указателей - стереть два контроллера-члена с помощью * 1044.*std::any
: Если это то, что возвращает фабрика, вы не будете иметь преимущества от использования виртуальных методов в базовом классе, но если у вас есть код, который знает, какой тип контроллера он должен получить - он может получить его безопасным для типов способом из std::any
.