如何在具有指向对象的指针数组的类中创建复制构造函数和析构函数,其中对象本身具有指向整数的指针数组
How to create copy constructor and destructor in a class with an array of pointers to objects where objects itself have an array of pointers to ints
在下面的代码中,我有一个类A,它有一个动态的整数数组。 我还有另一个类 B,它有一个指向类 A 对象的指针数组,我已经为 A 类编写了复制构造函数。 我需要为 B 类编写一个复制构造函数和析构函数,我已经尝试了各种方法但没有成功。
A类的定义:
class A {
public:
A::A(const A& other){
siz = other.siz;
c = other.c;
s = other.s;
e = other.e;
arr= new int[other.c];
memcpy(arr, other.arr, sizeof(int) * c);
}
A::~A() {
delete [] m_arr;
}
const A& operator=(const A& rhs){
if(this == &rhs)
return *this; // handling of self assignment
delete[] arr; // freeing previously used memory
arr = new int[rhs.c];
siz = rhs.siz;
c = rhs.c;
e = rhs.e;
s = rhs.s;
memcpy(m_arr, rhs.arr, sizeof(int) * c);
return *this;
}
private :
int *arr ;
int c ;
int siz ;
int s ;
int e ;
}
B类的定义:
class B {
public:
B::B(const B& other){
// .......need help here
}
B::~B() {
//......need help here
}
private :
static const int constant = 7;
A * array[constant] ;
int x ;
int y ;
int z ;
}
感谢您的帮助
关键是将原始拥有指针包装到 RAII 资源管理器中,并定义组装这些安全RAII构建块的类。然后,C++编译器将能够自动合成复制操作和析构函数(以及移动操作)。
例如,在class A
中,您有一个int *arr
数据成员,用于存储指向动态分配数组的拥有原始指针。将其替换为 RAII 资源管理器,如标准std::vector
容器(例如std::vector<int> arr
)。
这样做,无需定义显式析构函数,因为编译器将自动调用矢量数据成员上的std::vector
析构函数,并且内存将自动释放(无需调用显式delete[]
)。
同样,默认的复制构造函数将执行成员级复制,因此std::vector
复制构造函数将由C++编译器自动调用,并且从源向量到目标向量的深层复制将自动发生,而无需您使用new[]
动态分配、memcpy
等"重新发明轮子"。
从 C++11 开始,您可以告诉C++编译器使用此语法合成默认复制构造函数(也可用于默认构造函数、移动构造函数、复制赋值运算符等)
class A {
public:
...
A(const A&) = default;
...
};
class B
也是如此,而不是A * array[constant]
数据成员,考虑使用vector
,例如指向 A 的智能指针向量:
vector<shared_ptr<A>> arrayOfAs;
另一种方法是将std::array
用于恒定大小的东西。
无论如何,关键是:考虑使用 RAII 资源管理器作为更复杂的类的构建基块,而不是将原始拥有指针和其他不安全的原始资源作为数据成员。每个原始资源都应安全地包装在其自己的 RAII 资源管理器中,这反过来又应用作更复杂的类的数据成员。
附言作为奖励阅读,请考虑阅读"什么是三法则? ",以及这种跟进。
让我们首先假设对于这个练习,无论出于何种原因,您都不能使用std::vector
或std::array
。 假设我们到目前为止遵循您的类的设计,我们可以实现如下的复制构造函数:
B::B(const B &other)
{
for (std::size_t i = 0; i < constant; ++i) {
// Use `new` to allocate memory and also call `A`'s copy constructor.
array[i] = new A(*other.array[i]);
}
}
它的作用,因为array
是一个指向A
s 的指针数组,是为数组中的每个元素分配动态内存,并使用new
填充数组中指向这些动态分配的A
对象的指针,同时还调用您为A
创建的复制构造函数,通过使用语法new A(other_a)
, 这叫你的A::A(const A &other)
. 由于other
是对A
的引用,而不是对A
的指针,因此other.array[i]
中持有的指针被取消引用,这就是为什么调用是A(*other.array[i])
,而不是A(other.array[i])
。
由于我们在这里分配了动态内存new
,析构函数必须为我们对 'new 的每次调用调用delete
。 这可以类似地实现:
B::~B()
{
for (std::size_t i = 0; i < constant; ++i) {
// As each `A` has been allocated with `new`, they should now be
// destroyed.
delete array[i];
}
}
因此,我们现在拥有的东西似乎可以按照我们的意愿工作,我们可能会认为这就是它的全部。 但是,事情开始变得复杂,因为如果new
执行的其中一个分配失败并引发异常,会发生什么? 或者如果A
的构造函数抛出异常怎么办?delete
永远不会被要求使用迄今为止已分配new
的元素。
为了使我们的复制构造函数异常安全,需要一些稍微复杂的代码。 例如,像这样:
B::B(const B &other)
{
std::size_t i;
try {
for (i = 0; i < constant; ++i) {
array[i] = new A(*other.array[i]);
}
} catch (...) {
// Delete all elements allocated so far
for (std::size_t d = 0; d < i; ++d) {
delete array[i];
}
// Re-throw the exception to the caller
throw;
}
}
像这样的代码很快就会变得无法维护。 为了避免此类问题,一个好的准则是,管理必须创建和销毁的资源的生存期应由仅管理该资源的生存期的类封装。 这很重要,因为如果您开始向类添加更多与此数组类似的构造,那么您的类将负责构造和破坏更多,而不仅仅是此数组,这将使异常安全比现在更加困难。 事实上,构造和析构数组已经相当复杂的原因是因为你的类负责 7 个独立资源(动态分配的对象)的生存期,每个数组元素一个。
考虑到这一点,简化此操作的一种方法是使用一个类来封装具有new
和delete
的对象动态分配和解除分配。 C++11 包括几个至少封装了释放部分的类,最相关的是std::unique_ptr
和std::shared_ptr
。 但是,这些类旨在避免复制。unique_ptr
是显式不可复制的,复制shared_ptr
只会创建对同一资源的新引用,同时保留引用计数。 这意味着您仍然必须手动实现复制。
您可以通过将B
中的声明从以下更改来切换到unique_ptr
:
A *array[constant];
自:
std::unique_ptr<A> array[constant];
然后,您可以使用以下内容填充复制构造函数中的每个成员:
array[i] = std::unique_ptr<A>(new A(*other.array[i]));
使用此方法,您将不再需要担心捕获异常,因为如果在构造函数中的某个位置引发异常,则会自动为数组中的每个unique_ptr
调用析构函数。 尚未分配给的unique_ptr
默认情况下将保留空指针,并且在销毁它们时将安全地不执行任何操作。
然而,还有另一种方法:根本不使用指针/动态内存。 你已经有一个类(A
)负责它自己的资源的生存期。
为此,可以将以下声明更改为B
:
A *array[constant];
自:
A array[constant];
这意味着您不再需要定义复制构造函数(或复制赋值运算符)。 如果在 C++ 类中未提供复制构造函数,则可以复制该类,就好像它具有一个简单的成员复制构造函数一样,该构造函数也适用于数组,并将为数组中的每个元素调用复制构造函数。 由于数组本身是类的一部分,并且没有指向动态内存的指针,因此不需要使用delete
手动释放每个元素。
- 指向指向字符数组的指针数组的指针
- 通过指向指针数组的指针访问子类的属性
- C++,指针数组,指向双链表中的条目
- 在C++中,如何初始化指向wchar_t*的指针数组(生成wchar_t**)
- C++从函数指针数组调用函数
- 关于指向指针数组的指针
- 将返回值存储在函数指针数组的指针中是如何工作的?
- 将链表转换为指针数组时出错
- C++ 对象指针数组的复制构造函数
- C++ - 循环访问指针数组会导致错误
- 删除指针数组 (C++) 中的元素
- 如何循环访问 cpp 中的函数返回的字符指针数组
- 将函数指针数组中的函数指针作为模板参数传递
- Google Or-Tools Glop:如何创建指向 const 对象的指针数组?
- 具有推导参数的模板函数指针数组变量
- 如何模板化堆栈分配的多态指针数组到接口,包括派生类型的相应点?
- C++指针数组到字符数组中的特定位置
- 如何在C++中复制指针数组的数据
- 初始化类中的指针数组,并在另一个类中检索它
- 尽管直接设置了指针数组,但仍为空