【问题标题】:Can a derived class be made uncopyable by declaring copy constructor/operator private in base class?可以通过在基类中声明复制构造函数/操作符私有来使派生类不可复制吗?
【发布时间】:2015-07-08 09:57:26
【问题描述】:

我认为理论上这个问题的答案是肯定的。

然而,实际上,我的编译器 (VS2010) 在以下情况下似乎没有抱怨:我有一个抽象基类,提供一些通用接口(但没有数据成员)以及从它派生的各种子类和子子类。

class Base 
{
public:
    Base() {}
    virtual ~Base() {}

    virtual void interfaceFunction1() = 0;
    virtual void interfaceFunction2() = 0;
private:
    Base(const Base&);            // all derived classes should be uncopyable
    Base& operator=(const Base&);

    // no data members
};

我的编译器发现即使在子类或子子类中实现完整的复制构造函数也不成问题。

如何确保从 Base 派生的每个类都是不可复制的?

编辑:如果我理解得很好,这正是 Scott Meyers 在 Effective C++(2005 年第 3 版)第 6 项中解释的他对 Uncopyable 类的想法(仅在此处扩展为一个完整的接口类)。使他的想法奏效的区别是什么? (我知道他是私下继承的,但这应该不成问题)

【问题讨论】:

  • 可能是添加一个虚拟数据成员。
  • 你测试过其他编译器吗?也提供一个派生类会很好;)(实际上更像是一个可编译的例子。)
  • 没有标记为virtual的纯函数有什么用?
  • @MarcoA。我能想到的唯一目的是触发编译错误。我会将其归结为“SO 过于简单化”。
  • 请发布 MCVE。这个should work.

标签: c++ abstract-class private copy-constructor base-class


【解决方案1】:

这应该防止编译器为没有显式声明的派生类生成复制构造函数。但是,没有什么能阻止派生类显式声明一个复制构造函数,该构造函数除了调用 Base 的复制构造函数之外还会做其他事情。

无法确保派生类可实例化但不可复制。

【讨论】:

  • 将 Base() 设为私有是否是一种解决方案? (但后来我不得不求助于静态工厂方法,这听起来很麻烦,只是为了使这些类不可复制)
  • @TG_ 不,那行不通。如果您离开 all Base 构造函数 private,则派生类将根本无法初始化自身(不是通过复制,不是默认情况下,也不是任何其他方式)。如果您让任何 Base 构造函数可访问,则派生类可以在其自己的副本 ctor 中使用它。
【解决方案2】:

而不是将复制构造函数/操作符声明为 private 将它们声明为已删除。将复制构造函数/操作符声明为私有并不是使派生类不可复制的最佳解决方案。如果您希望基类完全不可复制,则将复制构造函数/操作符声明为 deleted,因为复制仍然可以在 Base 的成员函数中作为私有进行该类的功能可以访问成员。可以使用C++11的delete功能:

Base(const Base&) = delete; // copy constructor
Base& operator=(const Base&) = delete; // copy-assignment operator

但是将复制构造函数/操作符声明为 private 也是正确的,只要您知道复制仍然可以发生在 Base 的成员函数中。 p>

【讨论】:

  • 将它们设为私有并声明为已删除具有相同的效果
  • @BЈовић 不一定,将它们设为私有并不能保证该类型的对象不可复制。如果我们将它们声明为已删除,则永远不会发生复制。 Effective Modern C++ 的第 11 项指出,如果尝试使用已删除的成员函数,则成员函数和友元函数中的代码将无法编译。将它们声明为私有意味着该特定类的成员函数和朋友内部的代码可以使用它们,并且由于缺少函数定义,链接将失败。
  • 默认的复制构造函数会创建新的基础对象,而不是尝试复制它。因此,如果派生类使用默认的复制构造函数,这种方法将不起作用,并且派生类将是可复制的,而基类则不是。
【解决方案3】:

在 C++11 及更高版本中,可以选择声明构造函数已删除。

struct X {
  X( const X& ) = delete;
};

现在,任何从 X 派生的依赖复制构造函数的东西都不会编译。 这在您想避免问题时最有用,因为编译器会自动生成构造函数...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-31
    • 2013-06-23
    • 2018-09-24
    • 2012-03-11
    • 2021-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多