C++中的数组类成员初始化

Sim*_*LIU 4 c++ arrays initialization

我有以下代码片段:

#include <iostream>
using namespace std;
class A {
    int* data;
    int size;
public:
    A(int s):size(s)
    {
        data = new int[size];
    }
    A() {
        data = nullptr;
    }
    ~A() {
        if (data) delete [] data;
    }
};
class B {
    A a[2];
public:
    B() {
        a[0] = A(10);
        a[1] = A(11); 
    }
};
int main(int argc, char *argv[]) {
    B b;
}
Run Code Online (Sandbox Code Playgroud)

在上面的C++代码中,我有一个类A,它有一个数组成员int* data,并且(de)内存分配由(de)构造函数处理.在我创建B类,其具有阵列A类固定长度作为一个数据成员的.

我的问题是:如何优雅地初始化会员A a[2]?在上面的代码中,在堆栈上创建A(10)A(11),当跳出作用域时,将调用它们的析构函数,因此数据无效.当跳转main函数的作用域时,持有的指针a[2]将被解除分配两次,从而导致错误:

pointer being freed was not allocated.

一种可能的解决方案是仔细设计a copy constructor和a move constructor,通过这样做,上述编码范例可以起作用.

我尝试过的另一个解决方案是在初始化列表中初始化数组class B:

B() : a { A(10), A(11) }
Run Code Online (Sandbox Code Playgroud)

这个解决方案有效,我并没有真正告诉初始化列表的基本机制.我认为它必须与简单的构造复制完全不同.我真的希望有些专家能够详细解释这种机制.当然,这种解决方案难以编码且不灵活.

所以我想知道C++中是否有一些编程范例可以解决这个设计问题?

eer*_*ika 5

在上面的代码中,在堆栈上创建A(10)和A(11)

它们是临时对象.它没有指定地方或创建它们,如果他们在所有创建.

当跳出范围时,他们的析构函数将被调用

在相应的移动赋值语句结束后,将调用每个临时的析构函数.

一种可能的解决方案是仔细设计复制构造函数和移动构造函数,通过这样做,上述编码范例可以工作.

并且{copy,move}赋值运算符也是如此.当隐式声明的那些做不正确时,你应该总是这样做.如果你在析构函数中删除了某些内容,他们就永远不会做正确的事情.

我尝试过的另一个解决方案是在B类的初始化列表中初始化数组

这个解决方案有效,我并没有真正告诉初始化列表的基本机制.我认为它必须与简单的构造和复制完全不同.

原始代码中的错误是表现不佳的移动赋值运算符A.由于初始化列表永远不会从临时移动分配,因此它永远不会触发错误.

这实际上a是你要求的更优雅的构建方式.不是因为它避免了这个错误,而是因为避免不必要的移动本质上就是好事.

所以我想知道C++中是否有一些编程范例可以解决这个设计问题?

是.RAII单一责任原则.除非你的类不做任何其他事情,除了管理指向的内存之外data,它不应该管理内存.相反,它应该将内存管理委托给RAII对象.在这种情况下,您应该使用std::vector成员.

class A {
    std::vector<int> data;
public:
    A(int s):data(s) {}
    A() = default;
};
Run Code Online (Sandbox Code Playgroud)