我刚开始学习Rule of Three
,并想知道以下方法是否足以支持复制构造函数:
Array<T, ROW, COL>(const Array<T, ROW, COL> &array) {
*this = array;
}
如果还不够,为什么不呢?
编辑为每个请求添加了分配运算符
inline Array<T, ROW, COL>& operator=(const Array<T, ROW, COL> &other) {;
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
data[j*ROW + i] = other(i, j);
}
}
return *this;
}
答案 0 :(得分:4)
一般说明(在您编辑问题之前):
在复制构造函数中调用复制赋值运算符不是一般解决方案。它们意味着不同的东西(我的意思是,如果它们的意思相同,为什么它们应该在那里)。
当(现有!)对象被赋予新值时,将调用赋值运算符。 如果此对象属于同一类型,我们也称之为复制赋值,因为典型的实现只是复制内容(但可能共享一些引用的东西,例如共享指针或带共享的PIMPL类)数据)。复制赋值运算符由编译器自动实现,除非您提供一个。它使用类型的赋值运算符复制每个成员(原始成员也被复制)。
当(尚未存在!)obejct被赋予相同类型的初始值时,将调用复制构造函数,即应使用现有对象的副本对其进行初始化。同样,如果您不提供复制构造函数,编译器会为您生成一个,再次使用复制构造函数复制成员。
如果从复制构造函数中调用赋值运算符,则表示生成的程序在复制初始化新对象时执行以下步骤:
因此 在大多数情况下都应该没问题,但有几种情况不工作,特别是如果你的班级有的成员>不能默认构造或无法分配。如果是这种情况,并且它们仍然可以复制构造(与复制分配相反),则必须手动初始化复制构造函数的成员初始化列表中的成员。
编辑(由于问题已被编辑):在您的情况下,data
是基本类型(所有指针都被认为是原始的),因此您必须在副本中正确初始化它调用赋值运算符之前的构造函数。如果不这样做,分配将删除未初始化的指针。所以你应该做到最好(避免代码重复;如果你这样做可能会更有效率):
Array<T, ROW, COL>(const Array<T, ROW, COL> &array) :
data(0) // Now it is at least initialized, although inefficient
{
*this = array;
}
然后,赋值运算符将尝试删除空指针,这是可以的(它什么都不做),然后执行实际的复制。将data(0)
初始化视为“默认构造的null Array<...>
”对象,暂时考虑。 (也许你已经提供了一个不分配外部存储器的空对象?)
答案 1 :(得分:3)
有一个重要原因导致你提出的代码不够:
data
成员未正确初始化*this = array
(data
不在成员初始化列表中),因此您正在调用delete[]
运算符未初始化的data
。它要求您至少在使用成员初始化程序列表之前初始化data
成员。
你的建议有更多的缺点:
T
类型(但可能限制在类Array
中):
T
必须是默认可构造的(如leemes所述)。a = a
(自我指派),正如Nick Bougalis所说。new[]
运算符失败,则对象将处于不一致状态。T
抛出异常,则该对象将处于不一致状态。在使用赋值运算符实现中的复制构造函数而不是相反的情况下,我看到了更好的方法:
Array<T, ROW, COL>(const Array<T, ROW, COL> &array): data(new T[ROW * COL]) {
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
data[j*ROW + i] = other(i, j);
}
}
}
Array<T, ROW, COL>& operator=(Array<T, ROW, COL> other) {
swap(other);
return *this;
}
void swap(Array<T, ROW, COL>& other) {
T* tmp = data;
data = other.data;
other.data = tmp;
}
(这也称为复制和交换习语。)
答案 2 :(得分:1)
除了leemes的优秀帖子之外,请允许我添加一个注意事项。在operator=
实现中,您应该考虑添加一个检查,以至少处理自我分配(即a = a;
)。此外,如果您的data
成员变量是一个指针(正如您的原始代码所示),那么您必须确保它指向一些有效的内存,因为如果您将其初始化为指向复制构造函数中的0
,当您的赋值运算符尝试取消引用它时,它将崩溃。
inline Array<T, ROW, COL>& operator=(const Array<T, ROW, COL> &other)
{
if(this == &other)
return *this; // nothing to do!
/* You should make sure that either data already points to an allocated
* buffer of the appropriate size, or you should allocate it at this point.
*/
assert(data != NULL);
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
data[j*ROW + i] = other(i, j);
}
}
return *this;
}