Mik*_*eyG 6 c++ inheritance constructor deep-copy assignment-operator
我最近重新访问了复制构造函数,赋值运算符,这里看到的复制交换idom: 什么是复制和交换习惯用法? 和许多其他地方 -
上面的链接是一个很好的帖子 - 但我还有一些问题 - 这些问题可以在很多地方,stackoverflow和许多其他网站上得到解答,但我没有看到很多一致性 -
1 - 您是否应该try- catch在我们为复制构造函数中的深层复制分配新内存的区域周围?(我已经看到了两种方式)
2 - 关于复制构造函数和赋值运算符的继承,何时应该调用基类函数,何时这些函数应该是虚函数?
3 - std::copy复制构造函数中复制内存的最佳方法是什么?我已经看过了memcpy,看到其他人说memcpy世界上最糟糕的事情.
考虑以下示例(感谢所有反馈),它提示了一些其他问题:
4 - 我们应该检查自我分配吗?如果是这样的话
5 - 关闭主题问题,但我看到swapped用作:
std::copy(Other.Data,Other.Data + size,Data);
应该是:
std::copy(Other.Data,Other.Data + (size-1),Data);
如果swap从'First到Last'而第0个元素是Other.Data?
6 - 为什么注释掉的构造函数不起作用(我必须将大小更改为mysize) - 假设这意味着无论我编写它们的顺序如何,构造函数将始终首先调用分配元素?
7 - 对我的实施还有其他评论吗?我知道代码没用,但我只想说明一点.
class TBar
{
public:
//Swap Function
void swap(TBar &One, TBar &Two)
{
std::swap(One.b,Two.b);
std::swap(One.a,Two.a);
}
int a;
int *b;
TBar& operator=(TBar Other)
{
swap(Other,*this);
return (*this);
}
TBar() : a(0), b(new int) {} //We Always Allocate the int
TBar(TBar const &Other) : a(Other.a), b(new int)
{
std::copy(Other.b,Other.b,b);
*b = 22; //Just to have something
}
virtual ~TBar() { delete b;}
};
class TSuperFoo : public TBar
{
public:
int* Data;
int size;
//Swap Function for copy swap
void swap (TSuperFoo &One, TSuperFoo &Two)
{
std::swap(static_cast<TBar&>(One),static_cast<TBar&>(Two));
std::swap(One.Data,Two.Data);
std::swap(One.size,Two.size);
}
//Default Constructor
TSuperFoo(int mysize = 5) : TBar(), size(mysize), Data(new int[mysize]) {}
//TSuperFoo(int mysize = 5) : TBar(), size(mysize), Data(new int[size]) {} *1
//Copy Constructor
TSuperFoo(TSuperFoo const &Other) : TBar(Other), size(Other.size), Data(new int[Other.size]) // I need [Other.size]! not sizw
{
std::copy(Other.Data,Other.Data + size,Data); // Should this be (size-1) if std::copy is First -> Last? *2
}
//Assignment Operator
TSuperFoo& operator=(TSuperFoo Other)
{
swap(Other,(*this));
return (*this);
}
~TSuperFoo() { delete[] Data;}
};
Run Code Online (Sandbox Code Playgroud)
如果分配内存,则需要确保在引发异常的情况下释放内存。您可以使用显式try/来执行此操作catch,也可以使用智能指针(例如std::unique_ptr保存内存),当智能指针被堆栈展开销毁时,该内存将被自动删除。
你很少需要virtual赋值运算符。如果您正在进行成员赋值,请在成员初始化列表中调用基类复制构造函数,并在派生赋值运算符中首先调用基类赋值运算符 --- 如果您正在进行复制/交换,则无需调用派生赋值运算符中的基类赋值,前提是复制和交换正确实现。
std::copy与对象一起工作,并将正确调用复制构造函数。如果您有普通的 POD 对象,那么也memcpy可以正常工作。不过,在大多数情况下我都会选择std::copy——无论如何,对于 POD 来说,它应该在memcpy幕后进行优化,并且它可以避免您稍后添加复制构造函数时可能出现的错误。
[更新问题的更新]
使用复制/交换编写时,不需要检查自分配,实际上也没有办法这样做——当您输入赋值运算符时,它other就是一个复制,并且您无法知道源对象是什么曾是。这只是意味着自分配仍然会进行复制/交换。
std::copy采用一对迭代器(first、first+size)作为输入。这允许空范围,并且与标准库中每个基于范围的算法相同。
注释掉的构造函数不起作用,因为成员按照声明的顺序进行初始化,而不管成员初始值设定项列表中的顺序如何。因此,Data总是首先初始化。如果初始化取决于,size那么它将获得一个 duff 值,因为size尚未初始化。如果你交换 的声明size,data那么这个构造函数将正常工作。好的编译器会警告成员初始化的顺序与声明的顺序不匹配。