简体   繁体   English

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

[英]C++ proper pointer member initialization

I'm new to C++ here, coming from a Java background. 我是Java新手,来自Java背景。 I have a class prototype that sets up two private pointer object members. 我有一个类原型,它设置了两个私有指针对象成员。

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;

Now, in my constructor for this class, I initialize these two variables using a factory design pattern that is provided to me by the API that I am using. 现在,在我的这个类的构造函数中,我使用由我正在使用的API提供给我的工厂设计模式初始化这两个变量。 This is the only real way to initialize these classes. 这是初始化这些类的唯一真正方法。

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;
  }

I know I'm doing something wrong with the allocation of memory here, because when I try to access these member pointers in later functions that are called, the program errors with a "Memory Permission Error". 我知道我在这里分配内存时做错了,因为当我尝试在后面调用的函数中访问这些成员指针时,程序错误带有“内存权限错误”。 I was looking into using unique_ptr to store the objects, as they manage life cycle well, however, since the only way to create the objects is through the factory initializer, I haven't been able to find a good way to construct the unique_ptr. 我正在研究使用unique_ptr存储对象,因为它们很好地管理生命周期,但是,由于创建对象的唯一方法是通过工厂初始化程序,我无法找到构造unique_ptr的好方法。

What is the proper way to initialize these pointer members? 初始化这些指针成员的正确方法是什么?

To make your pointers live as long as the object of DriverController live you can use std::unique_ptr instead of raw pointers. 要使你的指针生效,只要DriverController的对象存在,你可以使用std::unique_ptr而不是原始指针。

And as for the construction of the chassisController, since it is not copyable the solution for this can be done using C++17 copy-elision: 至于chassisController的构造,由于它不可复制,因此可以使用C ++ 17 copy-elision完成此解决方案:

chassisController = std::unique_ptr<okapi::ChassisControllerIntegrated> { new okapi::ChassisControllerIntegrated( okapi::ChassisControllerFactory::create( ...) )};

same thing for the profiler 探查器也是如此

Anyway, as other people commented and as the other factory is using references/values and not pointers, you might be better of storing both the controller and profiler as values. 无论如何,正如其他人评论并且另一个工厂使用引用/值而不是指针一样,您可能更好地将控制器和分析器存储为值。 But in order to store them as values you must initialize them in the contstuctor's initializer list as: 但是为了将它们存储为值,您必须在contstuctor的初始化列表中将它们初始化为:

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
    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)}
  {
   // no need to do anything in the body
  }  

Also very important detail is that the order of the data members defined needs to be the same as the order of the initialization in the constructor, ie since we use chassisController to initialize the chassisMotionProfiler , the chassisController needs to be declared before the chassisMotionProfiler 另外很重要的细节是,数据成员的顺序确定的需求是相同的构造函数初始化的顺序,即因为我们使用chassisController初始化chassisMotionProfiler ,该chassisController需要之前宣布chassisMotionProfiler

I'll first say this code looks very Java-esque: Objects which are "doers of things" (a controller which controls, a profile which profiles) - why not just control and profile when you need to? 我首先要说这段代码看起来非常像Java:对象是“事物的”(控制器控制,配置文件的配置文件) - 为什么不只是在需要时控制和配置? That might preclude the need for the factories. 这可能会排除对工厂的需求。

But ignoring this point, and assuming you do really need those points: 但是忽略了这一点,假设你真的需要这些点:

Have your factories return unique_ptr 's with custom deleters 让您的工厂使用自定义删除器返回unique_ptr

As commenters suggest, your factories act weird. 正如评论者所说,你的工厂很奇怪。 They seem to be returning values of type okapi::ChassisControllerIntegrated and okapi::AsyncMotionProfileController respectively (or types convertible to those two) - once you take their addresses. 它们似乎分别返回类型为okapi::ChassisControllerIntegratedokapi::AsyncMotionProfileController okapi::ChassisControllerIntegrated值(或者可转换为这两种类型的类型) - 一旦你获取它们的地址。 But that means the factories always return the same type, which defeats the purpose of having a factory in the first place (a factory can return a value of any type within some hierarchy via a pointer to the base class). 但这意味着工厂总是返回相同的类型,这就失去了首先拥有工厂的目的(​​工厂可以通过指向基类的指针返回某个层次结构中任何类型的值)。 If this were the case then, indeed, as @me' said - your created objects would be destroyed when leaving the constructor's scope. 如果情况确实如此,那么,正如@me'所说的那样 - 离开构造函数范围时,您创建的对象将被销毁。

If your factories were to return pointers to these two classes, the code would work, but it would be kind of a bad idea, since you would need to properly de-allocate the two pointed-to objects on destruction (or even send them to the factories for destruction). 如果你的工厂要返回指向这两个类的指针,那么代码就可以工作了,但这样做有点不好,因为你需要在销毁时正确地解除分配两个指向的对象(甚至将它们发送到破坏的工厂)。

@BobBills suggests one way of avoiding that, which is wrapping the two created pointers in std::unique_ptr 's. @BobBills提出了一种避免这种方法的方法,即将两个创建的指针包装在std::unique_ptr This works fine, but only if you can deallocate them naively. 这很好,但只有你可以天真地释放它们。

What I suggest is that you make the factories themselves return std::unique_ptr s, with the specific deleter function they need you to use. 我建议你让工厂自己返回std::unique_ptr ,并使用他们需要你使用的特定删除函数。 That you will really not have to worry about deletion at all - and neither will any other code using the factories. 您根本不必担心删除 - 并且任何其他代码也不会使用工厂。

The constructor code will be: 构造函数代码将是:

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)
    }
{ }  

(the same as with @BobBills ' solution) - and the benefit is that the destructor can safely be assumed to be trivial: (与@BobBills的解决方案相同) - 好处是可以安全地假设析构函数是微不足道的:

DriveController::~DriveController() = default;

Consider non-pointer-based alternatives 考虑非基于指针的替代方案

If your DeviceController code can know in advance all the different types of chassis controllers and profile controllers, you can indeed have your factory return a value - an std::variant , which can hold a single value of any of several fixed types, eg std::variant<int, double> can hold either an int or a double , but not both; 如果您的DeviceController代码可以事先知道所有不同类型的机箱控制器和配置文件控制器,您确实可以让您的工厂返回一个值 - 一个std::variant ,它可以包含几个固定类型中的任何一个的单个值,例如std::variant<int, double>可以包含intdouble ,但不能同时包含两者; and it takes up storage which is a bit more than the maximum storage over the different types. 它占用的存储量比不同类型的最大存储量略高。 That way you can avoid pointers altogether, and DeviceController will have non-pointer members for the chassis and profile controllers. 这样你可以完全避免指针, DeviceController将为机箱和配置文件控制器提供非指针成员。

Another way to avoid pointer use is to type-erase the two member controllers, using std::any : If that's what the factory returns, you won't have the benefit of using virtual methods on the base class, but if you have code which knows what controller type it should get - it can obtain it, in a type-safe manner, from an std::any . 避免使用指针的另一种方法是使用std::any来键入擦除两个成员控制器:如果这是工厂返回的内容,那么你将无法在基类上使用虚方法,但如果你有代码它知道应该获得什么样的控制器类型 - 它可以以类型安全的方式从std::any获取它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM