c ++继承私有拷贝构造函数:这怎么会产生编译时错误?

时间:2014-04-20 14:01:41

标签: c++ inheritance

在C ++中,如果我们有这个类

class Uncopyable{
public:
    Uncopyable(){}
    ~Uncopyable(){}
private:
    Uncopyable(const Uncopyable&);
    Uncopyable& operator=(const Uncopyable&);
};

然后我们有一个派生类

class Dervied: private Uncopyable{
};

我的问题是:当编译器在派生类中生成默认的复制构造函数和赋值运算符时,为什么不会生成编译时错误?生成的代码不会尝试访问基类私有成员吗?

6 个答案:

答案 0 :(得分:4)

C ++ 11 12.8 / 7状态"如果类定义没有显式声明复制构造函数,则会隐式声明一个。"所以Dervied有一个隐式声明的复制构造函数。 12.8 / 11说:

  

隐式声明的复制/移动构造函数是其类的inline public成员。如果X具有以下内容,则X类的默认复制/移动构造函数被定义为已删除(8.4.3):

     
      
  • 具有非平凡对应构造函数的变体成员,X是类似联合的类,

  •   
  • 类类型M(或其数组)的非静态数据成员,由于重载解析(13.3)而无法复制/移动,因为应用于M的相应构造函数,导致模糊或从默认构造函数中删除或无法访问的函数,

  •   
  • 无法复制/移动的直接或虚拟基类B因为重载解析(13.3),应用于B的相应构造函数,会导致歧义或函数从默认构造函数中删除或无法访问,

  •   
  • 具有析构函数的任何直接或虚拟基类或非静态数据成员,该析构函数已从默认构造函数中删除或无法访问,

  •   
  • 用于复制构造函数,rvalue引用类型的非静态数据成员,或

  •   
  • 用于移动构造函数,非静态数据成员或直接或虚拟基类,其类型不具有移动构造函数,并且不易于复制。

  •   

具体来说,第三个项目符号适用:Dervied具有无法复制的直接基类Uncopyable,因为重载解析会导致Dervied :: {{1}无法访问的函数}}。因此,Dervied(const Dervied&)隐式声明的复制构造函数被声明为已删除,当且仅当调用该复制构造函数时,才会导致编译时错误。

答案 1 :(得分:3)

当编译器在派生类中生成默认的复制构造函数和赋值运算符时,为什么不会产生编译时错误?

因为只有在编译代码需要它们时,编译器才会生成它们。使用包含复制构造函数和/或赋值运算符的派生类编写一些代码,您将看到正在查找的编译时错误。

答案 2 :(得分:2)

派生类将继承私有拷贝构造函数,但除非您复制派生类型的对象,否则不需要使用它,如本例所示。

编译器不会自动生成构造函数/运算符,除非它们使用 没有其他构造函数/运算符可用于执行该操作(即复制操作可以在移动操作就足够的某些情况下使用)。后一种陈述产生以下一套规则。

以下是自动生成某些成员函数的规则:

  • 默认构造函数(如果没有显式声明其他构造函数)

  • 如果没有移动构造函数或移动赋值运算符,则复制构造函数 明确声明。如果析构函数被声明为生成a 不建议使用复制构造函数。

  • 如果没有副本,则移动构造函数 构造函数,移动赋值运算符或析构函数是显式的 声明。

  • 如果没有移动构造函数或移动赋值,则复制赋值运算符 运算符是显式声明的。如果声明了析构函数 不推荐生成复制赋值运算符。

  • 如果没有复制构造函数,复制赋值运算符,则移动赋值运算符 或析构函数是显式声明的。

  • 析构

该列表取自this Wikipedia page

答案 3 :(得分:1)

继承中的private使private成为Derived,它仍然可以看到它们,使用Derived的类不能。{/ p>

答案 4 :(得分:1)

一个类不能在另一个类上调用私有方法,但它可以继承尽可能多的编码。此代码仅包含Derived中Uncopyable的成员函数。

想象一下,如果你编写了一个继承自std :: vector的类。您仍然可以擦除,插入,推送和执行所有这些操作。因为它们都是公共或受保护的向量成员函数,所以它们又调用特定于实现的私有函数来执行管理内存等低级操作。您在此派生类中的代码无法直接调用这些内存管理函数。这用于确保向量的创建者可以自由地更改内部细节,而不会破坏您对该类的使用。

如果您的示例是代码实际上的样子,那么这是用于制作无法复制的内容的常见模式。它会使以下代码产生编译器错误:

Derived Foo;
Derived Bar;
Foo = Bar

它还会使代码在以下内容中引发错误:

int GetAnswer(Derived bar)
    { /* Do something with a Derived */ }
Derived Foo;
int answer = GetAnser(Foo);

此示例失败,因为foo的副本已作为GetAnswer函数中的参数传递并传递。

有些东西可能无法复制的原因有很多。我经历过的最常见的是对象管理一些低级资源,一个文件,一个opengl上下文,一个音频输出等等...想象一下,如果你有一个管理日志文件的类。如果它在解构器中关闭了文件,那么当销毁副本时原始文件会发生什么。

编辑:将Uncopyable类传递给函数,通过引用传递它。以下功能不会复制:

int GetAnswer(Derived& bar)
    { /* Do something with a Derived */ }
Derived Foo;
int answer = GetAnser(Foo);

如果所有构造函数都是私有的并且实例化了类,那么它也会导致编译器错误。但即使所有成员函数甚至构造函数都是私有的,并且该类从未实例化,这将是有效的编译代码。

编辑:具有构造函数的类的原因是可能有其他方法来构造它,或者它可能具有静态成员函数或类函数。

有时工厂用于构建没有明显构造函数的对象。这些可能具有创建umakeable类实例所需的任何魔法的功能。我见过的最常见的只是有另一个公开的构造函数,但不是一个显而易见的地方。我也看到工厂作为unconstructable类的朋友类,所以他们可以调用构造函数,我已经看到代码手动旋转内存并将指针转换为内存的实例。所有这些模式都用于确保正确创建类,而不仅仅是C ++提供的保证。

我见过的一个更常见的模式是类中的静态成员函数。

class uncreateable
{
    uncreateable() {}
  public:
    static int GetImportantAnswer();
};

可以看出,我不仅需要创建类的实例来调用GetImportantAnswer(),而且如果需要的话我也无法创建实例。我可以使用以下代码调用此代码:

int answer;
answer = uncreateable::GetImportantAnswer();

编辑:拼写和语法

答案 5 :(得分:0)

嗯,实际上这个程序不能用g ++编译:

#include <iostream>
using namespace std;

class Uncopyable{
  public:
    Uncopyable(){}
    ~Uncopyable(){}
  private:
    Uncopyable(const Uncopyable&) {cout<<"in parent copy constructor";}
    Uncopyable& operator=(const Uncopyable&) { cout << "in parent assignment operator";}
};

class Derived: private Uncopyable{

};

int main() {
  Derived a;
  Derived b = a;
}

编译器输出:

$ g++ 23183322.cpp 
23183322.cpp:10:88: warning: control reaches end of non-void function [-Wreturn-type]
    Uncopyable& operator=(const Uncopyable&) { cout << "in parent assignment operator";}
                                                                                       ^
23183322.cpp:13:7: error: base class 'Uncopyable' has private copy constructor
class Derived: private Uncopyable{
      ^
23183322.cpp:9:5: note: declared private here
    Uncopyable(const Uncopyable&) {cout<<"in parent copy constructor";}
    ^
23183322.cpp:19:15: note: implicit copy constructor for 'Derived' first required here
  Derived b = a;
              ^
1 warning and 1 error generated.