struct List {
int size;
int* items;
List& operator=(const List& l);
};
List& List::operator=(const List& l)
{
delete[] items;
size = l.size;
items = new int[20];
for (int i = 0; i < size; i++)
items[i] = l.items[i];
return *this;
}
ostream& operator<<(ostream& out, const List& l)
{
out << "Size: " << l.size << "t {";
for (int i = 0; i < l.size; i++)
out << l.items[i] << " ";
return (out << "}");
}
int main()
{
int size = 6;
List l1 = { size, new int[size]{ 0, 1, 2, 3, 4, 5 } };
List l2 = l1;
l2.items[1] = 50;
cout << l1 << endl;
cout << l2 << endl;
return 0;
}
我很困惑,因为当我分配 l2 = l1 时,通过使用重载的操作员,为什么L1的内容在以后更改L2时会更改?特别是因为L1作为const通过。他们以某种方式指向内存中的同一对象,而不是作为副本。
List l2 = l1;
不调用复制分配操作员(operator=(const List& l)
)。由于"分配"发生在变量声明期间,因此您是通过复制初始化初始化l2
来调用编译器生成的默认复制构造函数。由于它做了浅的复制两个对象都将指向相同的数据。
如果您要编写自己的课程来管理内存/资源,则至少需要提供自己的复制构造函数,复制分配运营商和毁灭者。这被称为三个规则。如果要包含移动语义,则需要提供一个移动构造函数和移动分配操作员,该操作员被称为5的规则。也有零规则,其中使用已经"做正确的事"的使用类型(就像使用std::vector
)允许编译器生成的默认值适用于您,并且您可以按照三/五/零
List l2 = l1;
尽管有=
,因为这是A 声明您正在执行复制构造(正式的"复制initialisation"),这与分配操作员无关。
您没有定义复制构造函数(看起来很像您的复制分配运算符),因此指针确实是共享的。
如果您写信的话,结果将是您所期望的:
List l2{};
l2 = l1;
顺便说一句,如果我是您,我将提供size
和items
默认值(0
和nullptr
)。否则,当您忘记{}
时,成员具有未指定的值,所有地狱都会松动。这可以使用一个不错的默认构造函数,也可以通过忘记整个企业并使用std::vector
来完成;)
List l2 = l1;
调用编译器生成的复制构造函数,它不深复制指针成员。
如果要使用std::vector
作为items
,则可以将构造函数和分配运算符留给编译器。(您的代码中没有任何delete[]
调用,这意味着您的班级会像漏勺一样泄漏内存。)