我最近重新访问了复制构造函数,赋值运算符,复制交换idom,如下所示:
What is the copy-and-swap idiom?
和许多其他地方-
上面的链接是一个很好的帖子-但我还有其他问题-
在stackoverflow和许多其他站点上的很多地方都回答了这些问题,但是我还没有看到很多一致性-
1-在我们为拷贝构造函数中的深层拷贝分配新内存的区域周围,是否应该有try
-catch
? (我已经双向看过了)
2-关于复制构造函数和赋值运算符的继承,何时应调用基类函数,何时应将这些函数虚拟化?
3-std::copy
是在复制构造函数中复制内存的最佳方法吗?我已经用memcpy
看到过它,并且看到其他人说memcpy
是地球上最糟糕的事情。
考虑下面的示例(感谢所有反馈),它提示了一些其他问题:
4-我们应该检查自我分配吗?如果是这样的话
5-非主题问题,但我看到swaped用作:std::copy(Other.Data,Other.Data + size,Data);
应该是:std::copy(Other.Data,Other.Data + (size-1),Data);
如果交换是从“第一个到最后一个”,第0个元素是Other.Data?
6-为什么注释掉的构造函数不起作用(我必须将size更改为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;}
};
最佳答案
try
/catch
来执行此操作,也可以使用诸如std::unique_ptr
之类的智能指针来保存内存,当智能指针被堆栈解卷破坏时,该内存将被自动删除。 virtual
赋值运算符。在成员初始化列表中调用基类拷贝构造函数,如果要进行成员分配,则首先在派生的赋值运算符中调用基类赋值运算符-如果您要进行复制/交换,则无需调用您的派生赋值运算符中的基类赋值,前提是正确实现了 copy-and-swap 。 std::copy
适用于对象,并将正确调用拷贝构造函数。如果您有普通的POD对象,则memcpy
也可以正常工作。尽管在大多数情况下,我还是会选择std::copy
的---无论如何它都应该针对POD在后台进行优化,使其成为memcpy
,并且避免了以后再添加复制构造函数时可能出现的错误。 [更新更新的问题]
other
时,它就是拷贝,您无法知道源对象是什么。这仅意味着自我分配仍将执行复制/交换。 std::copy
将一对迭代器(first,first + size)作为输入。这允许空范围,并且与标准库中的每个基于范围的算法相同。 Data
总是首先被初始化。如果初始化依赖于size
,则它将获得duff值,因为size
尚未初始化。如果您交换size
和data
的声明,则此构造函数将正常工作。好的编译器会警告成员初始化的顺序与声明的顺序不匹配。 关于c++ - 复制构造函数和赋值运算符的实现选择-,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11208772/