为什么C ++允许在具有指针成员的类中编译默认的复制构造函数/赋值运算符重载?

时间:2013-01-11 23:30:31

标签: c++ copy-constructor assignment-operator

在使用C ++时,我注意到赋值操作符/对象复制的标准编译器行为是一个痛苦的屁股。虽然我可以理解当类的每个成员都有定义/默认的复制/赋值行为时的默认代码生成,但我不知道为什么编译器会冒险生成以哑方式复制指针的代码没有单一警告 ...为什么编译器在这种情况下决定用户?是否真的存在任何默认指针重写都有意义的情况?

@edit:

在此作为flamebait关闭之前,我想指出我不是故意咆哮。正如我在评论中所说,我还想问一下rule of three是否有例外。

6 个答案:

答案 0 :(得分:3)

  

我不知道为什么编译器会冒险生成以愚蠢的方式复制指针的代码

将指针作为成员的类没有任何问题,浅层复制这些类没有错。

凌乱的地方是资源所有权

动态内存分配是真正的罪魁祸首,没有指针成员。这只是一个单一的具体案例。其他可以包括锁,溪流,连接......

如果被指向的内存不属于该类,则没有区别。与文件流相同 - 您可以复制该类,并且两个副本都引用相同的流 - 但是什么时候关闭流?哪个副本关闭了? (“哪个副本删除了内存?”)

实施三条规则会让你彻底思考这个问题。

总而言之 - 我认为在任何地方都有警告是不现实的,你有一个指针或流或其他任何资源的成员 - 它们中有很多,而且大多数都没用。

答案 1 :(得分:2)

如果被指向的东西不属于有问题的类(并非完全不常见),那么浅拷贝就是你想要的。编译器无法从类定义中判断所有权语义是什么,因此它可以做出一个合理的猜测: Everything 被浅层复制。

如果您的类具有指针的所有权语义,那么您应该使用适当的智能指针(unique_ptrshared_ptr等)来记录所有权。

答案 2 :(得分:1)

普通(或哑)指针确实定义了复制和赋值行为。

如果您的指针始终指示所有权,则应使用智能指针。然后,复制和分配行为更可能是(或至少可以)你想要的。

答案 3 :(得分:1)

只是一个例子:
如果你有一个树数据结构,其中每个节点拥有它的子节点,那些子指针将是智能指针 - 但是如果你想保持指向你父节点的指针,那么它将是一个原始指针,因为它不管理生命周期它指向的是什么。

答案 4 :(得分:1)

速度和力量。

非天真的副本可能需要很长时间。如果这对您的程序来说是个问题,那么您选择退出三条规则是非常现实的。

欢迎使用C ++。你应该知道你在做什么。

答案 5 :(得分:0)

  

为什么编译器在这种情况下决定用户?

关键是编译器决定。他们让程序员决定。无论如何,你期待什么?要制作一份深刻的副本?这不太可能。请考虑以下事项:

// This isn't necessarily "good" code, but it's legal
struct MyStruct
{
    void* p;
    const char* str;
};

MyStruct m1, m2;

m1.p = (void*)&m2;
m1.array = "Hello world!";
m2 = m1; // Now what do you expect to happen?

C ++不会强迫您编写好的代码,因此上述内容完全合法。虽然上面的代码不是很好,但它确实显示了结构中指针的一些常见用例。将m1分配给m2时,您无法制作{{1}}的深层副本。如果深拷贝是默认行为,那将会很糟糕,因为上面的代码将无法编译。