为啥不继承 C++ 构造函数?

Posted

技术标签:

【中文标题】为啥不继承 C++ 构造函数?【英文标题】:Why aren't C++ constructors inherited?为什么不继承 C++ 构造函数? 【发布时间】:2011-07-15 16:27:11 【问题描述】:

为什么在这段代码中需要 Child 传递构造函数?我认为它不会,但是当我删除它时编译器(gcc 和 VS2010)会抱怨。有没有优雅的解决方法?将这个 shim 插入到子类中似乎毫无意义。

class Parent

public:
  Parent(int i)  
;

class Child : public Parent

public:
  Child(int i) : Parent(i)  
;

int main()

  Child child(4);
  return 0;

【问题讨论】:

是的,这样一个简单的例子似乎是合乎逻辑的。但是在更复杂的示例中,我不希望编译器仅仅因为它认为这是一个好主意就开始自动生成新的构造函数;这可能会导致我没想到会发生各种意外的自动转换。 Bjarne Stroustrop 说 (link),“在 C++98 中,我们可以将一组重载函数从基类‘提升’到派生类中……我说过‘只不过是一次历史事故阻止了使用它来为构造函数和普通成员函数工作。 C++0x 提供了这种功能……”他指的是using 语句,例如using Parent::Parent;,但我想知道总体上缺乏构造函数继承是否也是由于任意历史原因造成的。 是的,C++0x 提供了一些额外的功能。但它不是自动的,您需要明确表明您想要它。你想要的是一切自动化,这是一个糟糕的想法,因为它会导致你意想不到的自动类型转换。我相信它被考虑过(这就是为什么 C++0x 允许您使用 using Parent::Parent; 导入它们),但您的想法被拒绝了(但我没有报价)。 【参考方案1】:

因为以下是完全有效的:

class Parent

public:
    Parent(int i)  
;

class Child : public Parent

public:
    Child() : Parent(42)  
;

编译器如何猜测您是否希望派生子具有转发构造函数?并且在多重继承的情况下,两种类型的构造函数集可能不匹配;从基类 A 转发构造函数时,应该调用基类 B 上的哪个构造函数?

从技术上讲,我想语言设计者可能会说,如果该类型具有单个基类并且在没有显式构造函数的情况下,所有父构造函数都会被转发。但是,这会产生相反的问题:如果一个基类有多个构造函数,包括一个默认构造函数,并且子类希望只允许默认构造函数,那么现在必须明确指定。

【讨论】:

我知道构造函数本质上是静态方法,因此与常规方法不同,但我希望构造函数继承尽可能以相同的方式工作。例如,如果我的类不使用多重继承,那么调用哪个父构造函数就不会产生歧义,因为只有一个父构造函数——与常规方法相同。 但通常有充分的理由不想转发构造函数。您建议为一种特定的边缘情况添加自动构造函数生成的特殊处理,这会使语言变得复杂且已经很复杂(恕我直言)。 但是 C++ 会自动转发其他(公共和受保护的)方法。为什么将构造函数与其他方法区别对待? @plong:因为这会导致意外的类型转换。我喜欢我的类型转换是明确的或至少是预期的。【参考方案2】:

如果您没有明确指定应该调用哪个父构造函数,编译器将生成调用默认(无参数或具有所有默认值)构造函数的代码。

在您的情况下,Parent 类没有这样的构造函数,编译器将不知道 i 使用什么值。

未在类Child 中指定构造函数意味着将调用类Parent 中的默认构造函数,但该构造函数不存在。


我还没有尝试过,但请查看this section of the C++0x FAQ。我的印象是你所要求的在 C++0x 中是可能的。

【讨论】:

如果我在构造函数引用中传递参数,为什么编译器会查找默认构造函数?是的,看起来可以使用 C++0x 将父构造函数“提升”到子作用域中。【参考方案3】:

这是因为在 C++ 中你有多重继承。在下面的例子中,应该继承哪个构造函数?

class Parent1

public:
  Parent1(int i)  
;

class Parent2

public:
  Parent2(int i)  
;

class Child : public Parent1, public Parent2

;

【讨论】:

简单 - 它应该是 all 父构造函数的参数,按照它们被声明的顺序。当然是假设性的。我只是不认为这是正确的答案。 但是由于我没有使用多重继承,所以很明显我指的是哪个构造函数。【参考方案4】:

有没有优雅的解决方法?

从 C++11 开始,您可以使用 using declaration 从基类继承构造函数(不包括默认、复制或移动构造函数):

class Parent

public:
  Parent(int i)  
;

class Child : public Parent

public:
  using Parent::Parent; // inherits Parent(int)
;

int main()

  Child child(4);
  return 0;

【讨论】:

【参考方案5】:

这是另一个案例

class Parent

public:
  Parent(int i) this.i = i; 
  Parent() this.i = 0;
private:
  int i;
;

class Child : public Parent

public:
  Child(int i) :  
;

int main()

  Child child(4);
  return 0;

鉴于此,应该调用哪个 Parent 构造函数。如果Parent 定义了一个接受long 但没有接受int 的构造函数怎么办?编译器是否应该将int 自动转换为long

无论好坏,语言设计者都选择了简单性——从派生类自动调用的唯一构造函数是默认(无参数或所有参数都具有默认值)构造函数。其他所有内容都需要显式调用。

【讨论】:

使用 C++ 中的普通提升规则。为什么构造函数的处理方式与普通方法不同? 首先,您想要的行为正是正常方法的工作方式。如果我在派生类中重写foo(),它不会自动调用foo() 的基类版本。 现在,如果我在基类上定义foo(int x),则可以在派生类对象上按原样调用该函数。 原因是构造与调用函数是完全不同的操作。派生类比基类需要更多的参数来构造自己是有道理的。例如,不必计算它的面积。【参考方案6】:

编译器只会自动生成默认构造函数,即不带参数的 ctr,但前提是你没有定义任何构造函数。

在这种情况下,您有(在父级中),因此没有为您设置默认值,而且子级需要它,以便它知道如何处理您传递给它的参数 i

【讨论】:

【参考方案7】:

如果不想使用编译器生成的默认构造函数,则需要子构造函数显式调用构造函数,称为默认构造函数(不带参数的构造函数)。但显然在您的情况下,您想要一个接受int 的构造函数,那么您必须显式调用它。

【讨论】:

是的,我知道,但我想知道为什么会这样,以及是否有一个优雅的解决方法。显然不是。

以上是关于为啥不继承 C++ 构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 C++ 构造函数在继承中需要默认参数?

为啥构造函数在java中不被继承?

为啥 C++ 中 std::mutex 的构造函数不抛出?

C++:调用无参数的构造函数为啥不加括号

类 x(继承)C++ 不存在默认构造函数

为啥此 C++ 代码中的构造函数不明确,我该如何解决?