操作员重载=修改原始对象


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;

顺便说一句,如果我是您,我将提供sizeitems默认值(0nullptr)。否则,当您忘记{}时,成员具有未指定的值,所有地狱都会松动。这可以使用一个不错的默认构造函数,也可以通过忘记整个企业并使用std::vector来完成;)

List l2 = l1;调用编译器生成的复制构造函数,它不深复制指针成员。

如果要使用std::vector作为items,则可以将构造函数和分配运算符留给编译器。(您的代码中没有任何delete[]调用,这意味着您的班级会像漏勺一样泄漏内存。)

最新更新