确保正确维护 C++ 中的 Clone()

Posted

技术标签:

【中文标题】确保正确维护 C++ 中的 Clone()【英文标题】:Assuring proper maintenance of Clone() in C++ 【发布时间】:2010-07-01 22:29:09 【问题描述】:

在 C++ 中,我发现将“Clone()”方法添加到属于需要(尤其是多态)复制的层次结构的类中非常有用,其签名如下:

class Foo 
public:
virtual Foo* Clone() const;
;

这可能看起来很漂亮,但很容易出现类维护引入的错误。每当有人向此类添加数据成员时,他们必须记住更新 Clone() 方法。失败通常会导致细微的错误。单元测试不会发现错误,除非它们被更新或类具有相等比较方法并且既是单元测试又是更新以比较新成员。维护者不会得到编译器错误。 Clone() 方法看起来很通用,很容易被忽略。

是否有其他人因此而对 Clone() 有问题?我倾向于在类的头文件中添加注释,提醒维护者在添加数据成员时更新 Clone。那里有更好的主意吗?插入#if/etc 会很愚蠢/迂腐吗?指令来查找类大小的变化并报告警告/错误?

【问题讨论】:

【参考方案1】:

为什么不直接使用复制构造函数?

virtual Foo* Clone() const  return new Foo(*this); 

编辑: 哦,等等,Foo 是 BASE 类,而不是 DERIVED 类?原理相同。

virtual Foo* Clone() const  return new MyFavouriteDerived(*this); 

只要确保所有派生类都以这种方式实现克隆,就不会出现问题。私有复制构造函数没问题,因为方法是成员,因此可以访问它。

【讨论】:

我有时会使用受保护/私有的复制构造函数来实现克隆,但是复制构造函数不是多态的(客户端必须知道要复制的对象的类型),它只是推动了负担必须知道(并同时维护)类的所有数据成员到复制构造方法的一种方法。 @David:不。复制构造函数调用在虚拟方法中——它沿着链向上到达 Foo,而 Foo 知道它自己的类型。此外,如果有人维护了一个类并且没有更新复制构造函数,那么是时候解雇他们或接受更多培训了。 我赞成这个。这种方法应该有效。欲了解更多信息:parashift.com/c++-faq-lite/virtual-functions.html#faq-20.8. 我同意复制构造函数在维护者的眼球中更引人注目或更突出。我只是希望有一个万无一失的方法。这种错误很容易犯,除非你正在寻找它。我知道我自己的直觉是向类添加新成员和基于该成员的新操作是您可以对现有代码进行的最安全的更改之一,因此任何违背直觉的事情都可能是错误的来源从长远来看。 @dragon - 真的有人这样做吗?顺便说一句,当心协变返回类型和多重继承。至少有一个常见的编译器疯了。【参考方案2】:

好吧,这个问题让我很困惑。最初我以为您想自动创建 clone(),我打算将您定向到 discussion I was in a couple years back。

但是...这似乎不是您想要的。因此,我将仅解决您的假设:

每当有人向此类添加数据成员时,他们必须记住更新 Clone() 方法。

为什么?您肯定已经根据复制构造函数实现了 clone()。

struct X  X* clone() const  return new X(*this);  ;

单元测试不会发现错误,除非它们被更新或类具有相等比较方法,并且既经过单元测试又更新以比较新成员。

这应该不是问题,因为您肯定在添加成员之前更新了单元测试...

有没有其他人因为这个原因而对 Clone() 有问题?

不,我可以肯定地说,我从来没有遇到过你对 clone() 的特殊问题。

【讨论】:

复制时必须有人知道数据成员。如果您依赖编译器生成的复制构造函数,原始指针将被浅拷贝,如果它们在类的析构函数中被删除,则会导致错误。您是说真正的解决方案是确保所有成员都具有值语义(例如,将指向多态对象的原始指针包装在智能指针中,这些指针在执行其复制构造函数时对包装的指针使用克隆方法),以便编译器生成的复制构造函数会做正确的事吗? 谁说依赖编译器生成的复制构造函数???这是一个完全独立的问题。 不是问题(还),只是一个问题。我正在维护具有 20 个数据成员的其他人的课程。我是实现 Clone() 的人。这个项目中的一些类确实可以使用重构(很多愚蠢的价值持有者类),但在短期内,我的目标是不要让事情变得更糟。我同意显式复制构造函数比 Clone() 方法更可能得到正确维护。我只是希望有一种更简单的方法。我为响应您的 cmets 而想到的智能指针方法可能是最吸引我的方式。 让我直截了当地说...您正在维护一个包含 20 个元素的类,该类需要一个重要的复制构造函数,但它没有...并且您正在实现克隆( ) 这个类。差不多吧?我的建议:是误杀的时候了。 哈哈,@Noah,因为我们现在为不同的公司工作,所以错过了我在那个机会上的机会。在代码的防御中,在新的需求出现之前不需要复制,过去的一切都是通过反序列化一个 XML 文件来构建的。肯定有一个“机会”可以花大量时间进行重构。

以上是关于确保正确维护 C++ 中的 Clone()的主要内容,如果未能解决你的问题,请参考以下文章

C++ 中 clone() 的最佳签名是啥?

alloca() 可以替代 C++ 中的自动指针吗?

线程中的内存范围共享:确保数据不会卡在缓存中

c++ 将类成员推送到向量的正确方法

如何确保 Python 中的参数类型正确?

如何确保所有使用redux的promise中的promise顺序正确?