从派生类调用基类构造函数的C++规则是什么?

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


当前回答

如果您只想将所有构造函数参数传递给基类(=父类),这里是一个最小的示例。

这使用模板将每个带有1、2或3个参数的构造函数调用转发给父类std::string。

Code

Live版本

#include <iostream>
#include <string>

class ChildString: public std::string
{
    public:
        template<typename... Args>
        ChildString(Args... args): std::string(args...)
        {
            std::cout 
                << "\tConstructor call ChildString(nArgs="
                << sizeof...(Args) << "): " << *this
                << std::endl;
        }

};

int main()
{
    std::cout << "Check out:" << std::endl;
    std::cout << "\thttp://www.cplusplus.com/reference/string/string/string/" << std::endl;
    std::cout << "for available string constructors" << std::endl;

    std::cout << std::endl;
    std::cout << "Initialization:" << std::endl;
    ChildString cs1 ("copy (2)");

    char char_arr[] = "from c-string (4)";
    ChildString cs2 (char_arr);

    std::string str = "substring (3)";
    ChildString cs3 (str, 0, str.length());

    std::cout << std::endl;
    std::cout << "Usage:" << std::endl;
    std::cout << "\tcs1: " << cs1 << std::endl;
    std::cout << "\tcs2: " << cs2 << std::endl;
    std::cout << "\tcs3: " << cs3 << std::endl;

    return 0;
}

输出

Check out:
    http://www.cplusplus.com/reference/string/string/string/
for available string constructors

Initialization:
    Constructor call ChildString(nArgs=1): copy (2)
    Constructor call ChildString(nArgs=1): from c-string (4)
    Constructor call ChildString(nArgs=3): substring (3)

Usage:
    cs1: copy (2)
    cs2: from c-string (4)
    cs3: substring (3)

更新:使用变量模板

推广到n个参数并简化

        template <class C>
        ChildString(C arg): std::string(arg)
        {
            std::cout << "\tConstructor call ChildString(C arg): " << *this << std::endl;
        }
        template <class C1, class C2>
        ChildString(C1 arg1, C2 arg2): std::string(arg1, arg2)
        {
            std::cout << "\tConstructor call ChildString(C1 arg1, C2 arg2, C3 arg3): " << *this << std::endl;
        }
        template <class C1, class C2, class C3>
        ChildString(C1 arg1, C2 arg2, C3 arg3): std::string(arg1, arg2, arg3)
        {
            std::cout << "\tConstructor call ChildString(C1 arg1, C2 arg2, C3 arg3): " << *this << std::endl;
        }

to

template<typename... Args>
        ChildString(Args... args): std::string(args...)
        {
            std::cout 
                << "\tConstructor call ChildString(nArgs="
                << sizeof...(Args) << "): " << *this
                << std::endl;
        }

其他回答

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

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

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

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

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

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

在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_赋值更可取,因为这样可以节省额外的赋值成本。

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

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

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

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

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

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