在使用 C++ 时,我注意到赋值运算符/对象复制的标准编译器行为令人头疼。虽然当类的每个成员都具有已定义/默认复制/分配行为时,我可以理解默认代码生成,但我不知道为什么编译器会冒着生成以愚蠢方式复制指针的代码而没有单一警告的风险......在这种情况下,为什么编译器会为用户做出决定?真的有任何情况下默认指针重写是有意义的吗?
@编辑:
在这被作为诱饵关闭之前,我想指出我并不是要咆哮。正如我在评论中所说,我还想问一下三规则是否有例外。
在使用 C++ 时,我注意到赋值运算符/对象复制的标准编译器行为令人头疼。虽然当类的每个成员都具有已定义/默认复制/分配行为时,我可以理解默认代码生成,但我不知道为什么编译器会冒着生成以愚蠢方式复制指针的代码而没有单一警告的风险......在这种情况下,为什么编译器会为用户做出决定?真的有任何情况下默认指针重写是有意义的吗?
@编辑:
在这被作为诱饵关闭之前,我想指出我并不是要咆哮。正如我在评论中所说,我还想问一下三规则是否有例外。
我不知道为什么编译器会冒着生成以愚蠢方式复制指针的代码的风险
将指针作为成员的类没有错,浅拷贝这些类也没有错。
混乱的地方是资源所有权。
动态内存分配是真正的罪魁祸首,没有指针成员。这只是一个单一的具体案例。其他可以包括锁、流、连接......
如果指向的内存不属于该类,则没有区别。与文件流相同——你可以复制类并且两个副本都引用同一个流——但是你什么时候关闭流?哪个副本关闭它?(“哪个副本删除了内存?”)
拥有三个有效的规则会让你彻底思考这些东西。
总而言之-我认为在您拥有一个指针或流或您拥有的任何其他资源的成员的任何地方都有警告是不现实的-会有很多警告,而且大多数都没用。
如果所指向的东西不属于所讨论的类(一点也不罕见),那么浅拷贝正是您想要的。编译器无法从类定义中分辨出所有权语义是什么,因此它做出了一个合理的猜测:一切都是浅拷贝的。
如果您的类具有指针的所有权语义,那么您应该使用适当的智能指针(unique_ptr
、、shared_ptr
等)来记录所有权。
普通(或哑)指针确实具有定义的复制和分配行为。
如果您的指针始终指示所有权,您应该使用智能指针。然后,复制和分配行为更有可能(或至少可以成为)您想要的。
举个例子:
如果你有一个树形数据结构,其中每个节点都拥有它的子节点,那么这些子指针就是智能指针——但是如果你想保存一个指向父节点的指针,它就是一个原始指针,因为它是不管理它所指向的生命周期。
为了速度和力量。
非天真的副本可能需要很长时间。如果这对您的程序来说是一个问题,那么您选择退出三规则是非常现实的。
欢迎使用 C++。你应该知道你在做什么。
在这种情况下,为什么编译器会为用户做出决定?
关键是编译器不会决定。他们让程序员做出决定。无论如何,你期望什么?要制作深拷贝?这并不完全可能。考虑以下:
// 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
. 如果深拷贝是默认行为,那会很糟糕,因为上面的内容将无法编译。