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

时间:2020-03-06 14:35:50  来源:igfitidea点击:

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

例如,我知道在Java中,我们必须作为子类构造函数的第一行来执行此操作(如果不这样做,则假定对no-arg超级构造函数的隐式调用会给我们提供编译错误(如果缺少的话)。

解决方案

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

有关构造函数的初始化列表的更多信息,请参见此处和此处。

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

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

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

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

如果不使用C ++调用parent构造函数,则无法构造派生类。如果是非arg C'tor,则这会自动发生,如果我们直接调用派生的构造函数(如上所示),或者代码无法编译,则会发生这种情况。

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

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

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

在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块未引发其他异常,则运行时将重新引发原始错误;否则,将重新引发原始错误。初始化期间的异常不能忽略。

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

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

如我们所见,基类的构造函数在初始化列表中被调用。顺便说一句,在初始化列表中初始化数据成员比在构造函数的主体内分配b_和c_的值更可取,因为这样可以节省额外的分配成本。

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