c++ - 子类重载父类方法 - 调用超类构造函数的规则是什么?




子类调用父类构造函数 (6)

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

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

如果此时运行的任何内容抛出,先前完成构建的基础/成员将调用它的析构函数,并将异常重新抛出给调用者。 如果要在链接期间捕获异常,则必须使用函数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;
};

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

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

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


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


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

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


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

如您所见,基类的构造函数在初始化列表中调用。 顺便说一句,初始化初始化列表中的数据成员优于为构造函数的body中的b_和c_赋值,因为这样可以节省额外的赋值成本。

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


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

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;
}

输出是:1


如果没有参数,基类构造函数会自动调用。 如果要使用参数调用超类构造函数,则必须使用子类的构造函数初始化列表。 与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
        }
};

关于herehere的构造函数初始化列表的更多信息。


当一个类从多个类派生时,没有人提到构造函数调用的顺序。 序列如派生类所述。


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





constructor