C ++正确的指针成员初始化

用户名

我是C ++的新手,来自Java背景。我有一个设置两个私有指针对象成员的类原型。

class DriveController : Controller {

public:

DriveController(
    int8_t portTL_, int8_t portTR_, int8_t portBL_, int8_t portBR_, 
    double wheelSize_, double baseSize_);

private:
// Internal chassis controller
okapi::ChassisControllerIntegrated *chassisController;
okapi::AsyncMotionProfileController *chassisMotionProfiler;

现在,在该类的构造函数中,我使用工厂设计模式初始化这两个变量,该工厂设计模式由我使用的API提供给我。这是初始化这些类的唯一真实方法。

DriveController::DriveController(
    int8_t portTL_, int8_t portTR_, int8_t portBL_, int8_t portBR_, 
    double wheelSize, double baseSize) 
{
    // Initialize port definitions
    portTL = portTL_;
    portTR = portTR_;
    portBL = portBL_;
    portBR = portBR_;

    // Create chassis
    auto chassis = okapi::ChassisControllerFactory::create(
        {portTL, portBL}, // Left motors
        {portTR, portBR}, // Right motors
        okapi::AbstractMotor::gearset::red, // torque gearset
        {wheelSize, baseSize} // wheel radius, base width
    );
    chassisController = &chassis;

    auto profiler = okapi::AsyncControllerFactory::motionProfile(
        1.0, 2.0, 10.0, *chassisController);
    chassisMotionProfiler = &profiler;
  }

我知道这里的内存分配有问题,因为当我尝试在后来的函数中访问这些成员指针时,程序错误并显示“ Memory Permission Error”。我一直在研究使用unique_ptr来存储对象,因为它们可以很好地管理生命周期,但是,由于创建对象的唯一方法是通过工厂初始化程序,因此,我一直没有找到构造unique_ptr的好方法。

初始化这些指针成员的正确方法是什么?

刺柏

我首先要说的是,这段代码看起来很像Java:对象是“事物的执行者”(一个控制器来控制,一个配置文件来配置文件)-为什么在需要时不只是控制和配置文件?那可能排除了对工厂的需要。

但是忽略这一点,并假设您确实需要这些点:

让您的工厂unique_ptr使用自定义删除器返回

正如评论员所建议的那样,您的工厂表现怪异。一旦您获取了它们的地址,它们似乎分别返回类型okapi::ChassisControllerIntegrated和的okapi::AsyncMotionProfileController(或可转换为这两个类型的类型)。但是,这意味着工厂总是返回相同的类型,这违背了首先拥有工厂的目的(​​工厂可以通过指向基类的指针返回某个层次结构中任何类型的值)。如果确实如此,那么确实如@me'所说-离开构造函数的作用域时,您创建的对象将被破坏。

如果您的工厂要返回指向这两个类的指针,则代码会起作用,但这是个坏主意,因为您需要在销毁时正确地取消分配两个指向的对象(甚至将它们发送给破坏工厂)。

@BobBills建议了一种避免这种情况的方法,即将两个创建的指针包装在中std::unique_ptr这很好,但前提是您可以天真地取消分配它们。

我建议您让工厂自己返回std::unique_ptrs,并使用它们需要您使用的特定删除器功能。您实际上完全不必担心删除-其他任何使用工厂的代码也不必担心。

构造函数代码为:

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代码可以事先知道所有不同类型的机箱控制器和配置文件控制器,那么您的工厂确实可以返回一个值-an std::variant,该值可以包含多个固定类型中的任何一个的单个值,例如std::variant<int, double>可以包含aint或a double,但不能同时兼而有之;并且占用的存储空间比不同类型的最大存储空间要大一些。这样,您就可以完全避免使用指针,并且DeviceController机箱和Profile Controller会有非指针成员。

避免使用指针的另一种方法是使用以下方法对两个成员控制器进行类型擦除std::any:如果这是工厂返回的内容,则在基类上使用虚拟方法将不会带来好处,但是如果您的代码知道哪个控制器输入它应该获取的类型-它可以以类型安全的方式从那里获取它std::any

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章