在初始化列表中,我应该调用基类的默认构造函数吗?

23
class A : public B
{
  ...
}

// case I : explicitly call the base class default constructor
A::A() : B()
{
  ...
}

// case II : don't call the base class default constructor
A::A() // : B()
{
  ...
}

案例II是否等于案例I?

对我而言,我认为在案例II中未调用基类B的默认构造函数。然而,尽管仍然持有这种看法,我进行了一项测试,证明了相反的结果:

class B
{
public:
    B()
    {
        cout << "B constructor" << endl;
    }
};

class A : public B
{
public:
    A()
    {
        cout << "A constructor" << endl;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    A a;
    return 0;
}

// VS2008输出结果

B constructor
A constructor
Press any key to continue . . .
5个回答

28

在这两种情况下都会调用基类构造函数。

这里有一篇文章链接,提供更多相关信息。


12

如果 B 没有用户声明的构造函数,行为就会不同。比较如下:

struct SimpleAggregate {
  int a;
  float b;
};

struct ClassWrapper : SimpleAggregate {
  ClassWrapper() : SimpleAggregate() { }
};

ClassWrapper w;

现在,w.aw.b已经保证为零。如果你省略了对基类的显式初始化,它们会拥有不确定的值。


也许你不知道,尽管语法如此,但上述使用SimpleAggregate()实际上并没有调用默认构造函数。它只是对基类进行了值初始化(关于"值初始化"是什么,我们在Stackoverflow上有几个很好的答案),因为没有用户声明的默认构造函数可以调用。


2
这是一个非常重要的区别。这个应该被接受为答案。 - bartgol

9
如果基类构造函数不需要任何参数,则不需要在初始化列表中明确提及它。

6
为了完成学习并深入理解,你可以开始稍微修改一些东西。例如,当 B 没有默认构造函数时会发生什么?它甚至能编译吗?类似这样的其他轻微修改将提供很好的学习体验。
话虽如此,在我的经验中,通常最好这样做。
A::A() : B() { ... } 

than

A::A() { ... } 

因为前者更加明确,它会让你思考基类初始化时到底发生了什么。通过明确表述事情,您很可能会避免隐藏的行为。


1
我给你点赞是因为你提出了一些改善对这个主题理解的方法。 - Krythic
1
我给了一个+1,因为这回答了问题“应该…”。大多数其他答案是针对相关问题“必须…”的。 - Pete Forman

2

所有继承自其他类的类都必须调用基类的构造函数。只有在所有基类完全构造完成后,派生类才能被构造。所以是否调用基类的构造函数并不重要。如果你不调用,只要编译器可以确定存在默认构造函数,它就会被调用。否则,编译器将抛出错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接