为什么我们需要定义运算符或复制构造函数,因为这些是系统提供的默认构造函数? 请用例子解释一下
答案 0 :(得分:2)
当您需要与默认编译器提供的函数实现不同的行为时,您需要提供它们。
默认复制构造函数按值复制所有成员。默认赋值运算符按值分配所有成员。 在很多情况下,这正是您想要的,因此无需提供您自己的实现。但情况并非总是如此。
考虑这个人为的例子:
struct Broken {
Broken() : i(42), p(new int) { }
~Broken() { delete p; }
int i;
Int* p;
};
它将获得默认的编译器生成的副本ctor和operator =但在这种情况下它们不能做你想做的事 - 考虑一下:
int main()
{
Broken b1;
Broken b2 = b1;
} // you'll probably crash here with a double free
b1
在其i
成员中存储42,然后在堆上分配新的int并将地址存储在其p
成员中(假设地址为0x1234) )。
然后我们从b2
构建b1
,默认的复制构造函数愉快地将b2.i
指定为42 - 这很好。它还指定b2.p
具有值0x1234 - 这不是您想要的。现在两个对象都拥有指向同一内存的指针,并且它们的析构函数都将尝试delete
它。
因此,当b2
超出main
末尾的范围时,它会释放内存 - 到目前为止一直很好 - 但是b1
也超出范围并试图释放已经释放内存,你的程序现已破裂。
在这种情况下,您需要提供自己的operator =和copy ctor,它们不会天真地复制指针的值,而是复制指针所指向的任何内容并将其存储在新的块中。与原始内存不同的内存,以便两个对象都有自己唯一的副本。
还有很多其他的例子,但这是我能想到的最简单的。