C++ 调用超类构造函数的规则是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/120876/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 13:00:42  来源:igfitidea点击:

What are the rules for calling the superclass constructor?

c++inheritanceconstructor

提问by levik

What are the C++ rules for calling the superclass constructor from a subclass one?

从子类调用超类构造函数的 C++ 规则是什么?

For example, I know in Java, you must do it as the first line of the subclass constructor (and if you don't, an implicit call to a no-arg super constructor is assumed - giving you a compile error if that's missing).

例如,我知道在 Java 中,您必须将其作为子类构造函数的第一行(如果不这样做,则假定隐式调用无参数超级构造函数 - 如果缺少,则给您一个编译错误) .

回答by luke

Base class constructors are automatically called for you if they have no argument. If you want to call a superclass constructor with an argument, you must use the subclass's constructor initialization list. Unlike Java, C++ supports multiple inheritance (for better or worse), so the base class must be referred to by name, rather than "super()".

如果基类构造函数没有参数,则会自动为您调用。如果要使用参数调用超类构造函数,则必须使用子类的构造函数初始化列表。与 Java 不同,C++ 支持多重继承(无论好坏),因此必须通过名称而不是“super()”来引用基类。

class SuperClass
{
    public:

        SuperClass(int foo)
        {
            // do something with foo
        }
};

class SubClass : public SuperClass
{
    public:

        SubClass(int foo, int bar)
        : SuperClass(foo)    // Call the superclass constructor in the subclass' initialization list.
        {
            // do something with bar
        }
};

More info on the constructor's initialization list hereand here.

此处此处有关构造函数初始化列表的更多信息。

回答by puetzk

In C++, the no-argument constructors for all superclasses and member variables are called for you, before entering your constructor. If you want to pass them arguments, there is a separate syntax for this called "constructor chaining", which looks like this:

在 C++ 中,在进入构造函数之前,所有超类和成员变量的无参数构造函数都会被调用。如果您想向它们传递参数,则有一个单独的语法称为“构造函数链接”,如下所示:

class Sub : public Base
{
  Sub(int x, int y)
  : Base(x), member(y)
  {
  }
  Type member;
};

If anything run at this point throws, the bases/members which had previously completed construction have their destructors called and the exception is rethrown to to the caller. If you want to catch exceptions during chaining, you must use a function try block:

如果此时有任何运行抛出,则先前已完成构造的基/成员将调用其析构函数,并将异常重新抛出给调用者。如果要在链接期间捕获异常,则必须使用函数 try 块:

class Sub : public Base
{
  Sub(int x, int y)
  try : Base(x), member(y)
  {
    // function body goes here
  } catch(const ExceptionType &e) {
    throw kaboom();
  }
  Type member;
};

In this form, note that the try block isthe body of the function, rather than being inside the body of the function; this allows it to catch exceptions thrown by implicit or explicit member and base class initializations, as well as during the body of the function. However, if a function catch block does not throw a different exception, the runtime will rethrow the original error; exceptions during initialization cannotbe ignored.

在这种形式中,注意 try 块函数体,而不是在函数体内部;这允许它捕获隐式或显式成员和基类初始化以及函数体期间抛出的异常。但是,如果函数 catch 块没有抛出不同的异常,则运行时将重新抛出原始错误;初始化期间的异常不能被忽略。

回答by Dima

In C++ there is a concept of constructor's initialization list, which is where you can and should call the base class' constructor and where you should also initialize the data members. The initialization list comes after the constructor signature following a colon, and before the body of the constructor. Let's say we have a class A:

在 C++ 中,有一个构造函数初始化列表的概念,它是您可以并且应该调用基类的构造函数以及您还应该初始化数据成员的地方。初始化列表在冒号后面的构造函数签名之后,在构造函数的主体之前。假设我们有一个 A 类:


class A : public B
{
public:
  A(int a, int b, int c);
private:
  int b_, c_;
};

Then, assuming B has a constructor which takes an int, A's constructor may look like this:

然后,假设 B 有一个接受 int 的构造函数,A 的构造函数可能如下所示:


A::A(int a, int b, int c) 
  : B(a), b_(b), c_(c) // initialization list
{
  // do something
}

As you can see, the constructor of the base class is called in the initialization list. Initializing the data members in the initialization list, by the way, is preferable to assigning the values for b_, and c_ inside the body of the constructor, because you are saving the extra cost of assignment.

如您所见,在初始化列表中调用了基类的构造函数。顺便说一下,在初始化列表中初始化数据成员比在构造函数体内部为 b_ 和 c_ 赋值更可取,因为你节省了额外的赋值成本。

Keep in mind, that data members are always initialized in the order in which they are declared in the class definition, regardless of their order in the initialization list. To avoid strange bugs, which may arise if your data members depend on each other, you should always make sure that the order of the members is the same in the initialization list and the class definition. For the same reason the base class constructor must be the first item in the initialization list. If you omit it altogether, then the default constructor for the base class will be called automatically. In that case, if the base class does not have a default constructor, you will get a compiler error.

请记住,数据成员始终按照它们在类定义中声明的顺序进行初始化,而不管它们在初始化列表中的顺序如何。为了避免在数据成员相互依赖时可能出现的奇怪错误,您应该始终确保成员在初始化列表和类定义中的顺序相同。出于同样的原因,基类构造函数必须是初始化列表中的第一项。如果完全省略它,则基类的默认构造函数将被自动调用。在这种情况下,如果基类没有默认构造函数,您将收到编译器错误。

回答by TT_

Everybody mentioned a constructor call through an initialization list, but nobody said that a parent class's constructor can be called explicitly from the derived member's constructor's body. See the question Calling a constructor of the base class from a subclass' constructor body, for example. The point is that if you use an explicit call to a parent class or super class constructor in the body of a derived class, this is actually just creating an instance of the parent class and it is not invoking the parent class constructor on the derived object. The only way to invoke a parent class or super class constructor on a derived class' object is through the initialization list and not in the derived class constructor body. So maybe it should not be called a "superclass constructor call". I put this answer here because somebody might get confused (as I did).

每个人都通过初始化列表提到构造函数调用,但没有人说可以从派生成员的构造函数体中显式调用父类的构造函数。请参阅问题 从子类的构造函数体调用基类的构造函数, 例如。关键是,如果您在派生类的主体中使用对父类或超类构造函数的显式调用,这实际上只是创建了父类的实例,而不是在派生对象上调用父类构造函数. 在派生类的对象上调用父类或超类构造函数的唯一方法是通过初始化列表而不是在派生类构造函数体中。所以也许它不应该被称为“超类构造函数调用”。我把这个答案放在这里是因为有人可能会感到困惑(就像我一样)。

回答by CR.

The only way to pass values to a parent constructor is through an initialization list. The initilization list is implemented with a : and then a list of classes and the values to be passed to that classes constructor.

将值传递给父构造函数的唯一方法是通过初始化列表。初始化列表是用一个 : 实现的,然后是一个类列表和要传递给该类构造函数的值。

Class2::Class2(string id) : Class1(id) {
....
}

Also remember that if you have a constructor that takes no parameters on the parent class, it will be called automatically prior to the child constructor executing.

还请记住,如果您有一个在父类上不带参数的构造函数,它将在子构造函数执行之前自动调用。

回答by Nils Pipenbrinck

If you have a constructor without arguments it will be called before the derived class constructor gets executed.

如果您有一个没有参数的构造函数,它将在派生类构造函数被执行之前被调用。

If you want to call a base-constructor with arguments you have to explicitly write that in the derived constructor like this:

如果要使用参数调用基构造函数,则必须像这样在派生构造函数中显式地编写它:

class base
{
  public:
  base (int arg)
  {
  }
};

class derived : public base
{
  public:
  derived () : base (number)
  {
  }
};

You cannot construct a derived class without calling the parents constructor in C++. That either happens automatically if it's a non-arg C'tor, it happens if you call the derived constructor directly as shown above or your code won't compile.

如果不调用 C++ 中的父构造函数,则无法构造派生类。如果它是非 arg C'tor,这会自动发生,如果您如上所示直接调用派生构造函数或您的代码将无法编译,则会发生这种情况。

回答by edW

If you have default parameters in your base constructor the base class will be called automatically.

如果基类构造函数中有默认参数,基类将被自动调用。

using namespace std;

class Base
{
    public:
    Base(int a=1) : _a(a) {}

    protected:
    int _a;
};

class Derived : public Base
{
  public:
  Derived() {}

  void printit() { cout << _a << endl; }
};

int main()
{
   Derived d;
   d.printit();
   return 0;
}

Output is: 1

输出为:1

回答by Dynite

CDerived::CDerived()
: CBase(...), iCount(0)  //this is the initialisation list. You can initialise member variables here too. (e.g. iCount := 0)
    {
    //construct body
    }

回答by Krishna Oza

Nobody mentioned the sequence of constructor calls when a class derives from multiple classes. The sequence is as mentioned while deriving the classes.

当一个类派生自多个类时,没有人提到构造函数调用的顺序。序列与派生类时提到的一样。