数组类成员初始化(C++)
Array class member initialization in C++
我有以下代码片段:
#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;
}
在上面的C++代码中,我有一个类 A,它有一个数组成员int* data
,内存的(取消)分配由 (de)构造函数处理。 I 创建了类 B,该类具有固定长度的类 A数组作为数据成员。
我的问题是:如何优雅地初始化成员A a[2]
?在上面的代码中,A(10)
和A(11)
是在堆栈上创建的,当跳出范围时,它们的析构函数将被调用,因此数据无效。跳转main
函数的作用域时,a[2]
持有的指针将被释放两次,从而导致错误:
pointer being freed was not allocated
.
一种可能的解决方案是仔细设计一个copy constructor
和一个move constructor
,通过这样做,上述编码范式就可以工作。
我尝试过的另一个解决方案是在class B
初始化列表中初始化数组:
B() : a { A(10), A(11) }
这个解决方案有效,我并没有真正告诉初始化列表的底层机制。我认为它一定与简单的构造和复制有很大不同。我真的希望一些专家能够对这种机制进行详细的解释。当然,这个解决方案是丑陋的硬编码,并不灵活。
所以我想知道C++是否有一些编程范式来解决这个设计问题?
在上面的代码中,A(10) 和 A(11) 是在堆栈上创建的
它们是临时对象。没有指定它们的创建位置或是否创建它们。
跳出范围时,将调用它们的析构函数
每个临时的析构函数将在相应的移动分配语句结束后调用。
一种可能的解决方案是仔细设计复制构造函数和移动构造函数,这样上述编码范式就可以工作。
还有{复制,移动}赋值运算符。当隐式声明的没有做正确的事情时,你应该总是这样做。如果您删除析构函数中的某些内容,它们永远不会做正确的事情。
我尝试过的另一种解决方案是在 B 类的初始化列表中初始化数组
这个解决方案有效,我并没有真正告诉初始化列表的底层机制。我认为它一定与简单的构造和复制有很大不同。
原始代码中的错误是行为不佳 A
的移动赋值运算符。由于初始化列表从不从临时分配中移动分配,因此它永远不会触发错误。
这实际上是您要求的构造a
的更优雅的方式。不是因为它避免了错误,而是因为避免不必要的移动本质上是一件好事。
所以我想知道C++是否有一些编程范式来解决这个设计问题?
是的。RAII和单一责任原则。除非你的类不做任何其他事情,否则除了管理data
指向的内存之外,它不应该管理内存。相反,它应将内存管理委托给 RAII 对象。在这种情况下,应使用 std::vector
成员。
class A {
std::vector<int> data;
public:
A(int s):data(s) {}
A() = default;
};
使用初始值设定项列表构造 B::a,如下所示:
class B {
A a[2];
public:
B() : a({10, 11}){
}
};
理想的答案是强制A
使用移动而不是副本,或者在副本上为项目分配新空间。在两者中,最有效的是前者,所以我将在下面对其进行扩展:
强制移动可以通过两种方式完成:
- 删除复制构造函数和复制
operator=
,并实现自己的移动构造函数和operator=
- 始终如一地使用
std::move
和std::swap
。
其中,前者的优势在于您将无法意外复制该类,但对于后者,您正在移动的事实将更加明显。
要删除默认复制方法,请执行以下操作:
class A {
A( const A& a ) = delete;
A& operator =( const A& a ) = delete;
}
- 是否可以初始化不可复制类型的成员变量(或基类)
- C++使用整数的压缩数组初始化对象
- C++初始化基类
- 多成员Constexpr结构初始化
- 复制列表初始化的隐式转换的等级是多少
- 内联映射初始化的动态atexit析构函数崩溃
- 如何在C++中初始化嵌套类中的2个memeber
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- 没有用于初始化C++中的变量模板的匹配构造函数
- 在未初始化映射的情况下,将值插入到映射的映射中
- C++成员初始化
- 为什么在C++中首先初始化成员类
- 同时具有"聚合初始化"和"模板推导"
- 初始化具有非默认构造函数的std::数组项的更好方法
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 在C和C++中初始化结构中的数组
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 在函数内部的声明中初始化数组,并在外部使用它
- 继承:构造函数,初始化C++11中基类的类C数组成员