是否应该始终为具有原始指针成员的 class 中的深度复制指针定义一个复制构造函数?
Should one always define a copy constructor for deep copying pointers in a class with raw pointer members?
据我所知,理论上,如果 class 有一个原始指针成员,那么默认的复制构造函数将对该指针进行浅拷贝,这样当原始对象被销毁时,指针副本中的成员将删除它指向的值。这似乎意味着在我们出于某种原因想要限制复制的情况之外,任何具有原始指针成员的 class 都应该定义一个复制构造函数来对该指针进行深度复制。
我正在与一个受人尊敬的第三方 API 合作,我遇到了一个 class 有一个原始指针成员,但没有定义复制构造函数,这对我上面的理解产生了怀疑.我错过了什么吗?
更新:第三方告诉我这个class不是要复制的,因为对象代表一个视觉元素。他们指出他们应该制作一个私有的复制构造函数。
不,你没有遗漏任何东西。 一般。
在不知道你说的是哪个库以及 class 的语义是什么(记录或其他)的情况下,我犹豫要不要做出任何具体保证,但是,从表面上看,这听起来就像一个图书馆错误。
class 可能不拥有指针对象。如果它的构造函数不分配它,并且它的析构函数不取消分配它,则可能会出现这种情况。
除此之外,你是对的。
我认为 std::reference_wrapper
是可敬的 API(C++ 标准库)的一部分。它有一个复制构造函数,但不一定在实现代码中明确定义,因为它只是复制原始指针。所以你是:拥有指针成员并不总是意味着所有权。
作为一个反例,一个你有非拥有指针成员但仍然需要负责复制的例子,一个对象可以包含一个指向其自身一部分的指针。如果复制这样的对象导致其内部指针指向其他对象的一部分,那将是不好的。
综上所述,视情况而定。您必须经常需要一个已定义的复制构造函数。但绝对不总是。
对于 C++03,三定律(或 "rule of three")是一个经验法则,如果您需要析构函数、复制赋值运算符或一个拷贝构造函数,那么你可能需要这三个。
所以检查是否有复制赋值运算符或析构函数。在那种情况下,可能需要复制构造函数,但缺少。
在 C++11 及更高版本中,有些人通过包含移动赋值运算符和移动构造函数将其扩展为 五定律,而有些人则简化为 零定律,要求所有所有权都应通过智能指针或集合对象来表达。
在理想世界中,任何原始指针成员都可以被假定为非拥有指针。因为,如果你想要一个拥有指针,你会使用一个智能指针!
非拥有指针成员将指向一个对象,该对象的生命周期在 class 之外进行管理,并且应该(按设计)长于指针的生命周期。
复制 class 时的默认行为是副本也指向同一对象。通常这就是您想要的行为。如果不是,是的,你将不得不改变这种行为。
当然,我们并不生活在一个理想的世界中,有很多地方使用了拥有原始指针成员。在那种情况下,你是对的,默认的复制构造函数是不合适的(参见 Alf 关于 3/5 规则的回答)。
据我所知,理论上,如果 class 有一个原始指针成员,那么默认的复制构造函数将对该指针进行浅拷贝,这样当原始对象被销毁时,指针副本中的成员将删除它指向的值。这似乎意味着在我们出于某种原因想要限制复制的情况之外,任何具有原始指针成员的 class 都应该定义一个复制构造函数来对该指针进行深度复制。
我正在与一个受人尊敬的第三方 API 合作,我遇到了一个 class 有一个原始指针成员,但没有定义复制构造函数,这对我上面的理解产生了怀疑.我错过了什么吗?
更新:第三方告诉我这个class不是要复制的,因为对象代表一个视觉元素。他们指出他们应该制作一个私有的复制构造函数。
不,你没有遗漏任何东西。 一般。
在不知道你说的是哪个库以及 class 的语义是什么(记录或其他)的情况下,我犹豫要不要做出任何具体保证,但是,从表面上看,这听起来就像一个图书馆错误。
class 可能不拥有指针对象。如果它的构造函数不分配它,并且它的析构函数不取消分配它,则可能会出现这种情况。
除此之外,你是对的。
我认为 std::reference_wrapper
是可敬的 API(C++ 标准库)的一部分。它有一个复制构造函数,但不一定在实现代码中明确定义,因为它只是复制原始指针。所以你是:拥有指针成员并不总是意味着所有权。
作为一个反例,一个你有非拥有指针成员但仍然需要负责复制的例子,一个对象可以包含一个指向其自身一部分的指针。如果复制这样的对象导致其内部指针指向其他对象的一部分,那将是不好的。
综上所述,视情况而定。您必须经常需要一个已定义的复制构造函数。但绝对不总是。
对于 C++03,三定律(或 "rule of three")是一个经验法则,如果您需要析构函数、复制赋值运算符或一个拷贝构造函数,那么你可能需要这三个。
所以检查是否有复制赋值运算符或析构函数。在那种情况下,可能需要复制构造函数,但缺少。
在 C++11 及更高版本中,有些人通过包含移动赋值运算符和移动构造函数将其扩展为 五定律,而有些人则简化为 零定律,要求所有所有权都应通过智能指针或集合对象来表达。
在理想世界中,任何原始指针成员都可以被假定为非拥有指针。因为,如果你想要一个拥有指针,你会使用一个智能指针!
非拥有指针成员将指向一个对象,该对象的生命周期在 class 之外进行管理,并且应该(按设计)长于指针的生命周期。
复制 class 时的默认行为是副本也指向同一对象。通常这就是您想要的行为。如果不是,是的,你将不得不改变这种行为。
当然,我们并不生活在一个理想的世界中,有很多地方使用了拥有原始指针成员。在那种情况下,你是对的,默认的复制构造函数是不合适的(参见 Alf 关于 3/5 规则的回答)。