您可以安全地从初始化列表中调用继承的成员函数,前提是该函数所做的任何事情都与该继承级别的成员数据的初始化方式无关。而且,因为size()
不依赖任何成员数据,它所做的只是返回一个文字
int size()
{ return 5; }
您的代码可以与任何编译器一起使用。所以甚至不需要base_size()
在初始化列表中
derived() : base_size(), base_implement(size())
在这种情况下。
然而,切换到一个更现实的例子,其中有一个初始化实例变量(即成员数据)的构造函数,在初始化列表中base_size
会更有意义:base_size()
class base_size
{
public:
base_size ()
{ _size = 5; } // initialization
int size()
{ return _size; }
private:
int _size; // instance variable
};
class base_implement
{
public:
base_implement(int s) : _vec(s)
{
cout << "size : " << _vec.size() << endl;
}
private:
vector<float> _vec;
};
class derived :
public base_implement,
public base_size
{
public:
derived() : base_size(), base_implement(size())
{
// crash
}
};
而且,在这个特定的例子中,程序会崩溃,因为vector
不会收到一个有效的值来分配它的大小。原因是您在所谓的base-specifier-list 中拥有的基类的顺序:
public base_implement,
public base_size
提到权威,这是标准在第 12.6.2 节“初始化基础和成员”中所述的内容
初始化应按以下顺序进行:
- 首先,并且仅对于如下所述的最派生类的构造函数,虚拟基类应按照它们在基类的有向无环图的深度优先从左到右遍历中出现的顺序进行初始化,其中“左-to-right”是派生类基说明符列表中基类名称的出现顺序。
- 然后,直接基类应按照它们出现在 base-specifier-list 中的声明顺序进行初始化(不管 mem-initializers 的顺序如何)。
- 然后,非静态数据成员应按照它们在类定义中声明的顺序进行初始化(同样不管 mem-initializers 的顺序)。
- 最后,执行构造函数的主体。
所以如果你要更换
public base_implement,
public base_size
和
public base_size,
public base_implement
这一切都会正确初始化,并且程序可以在大多数符合标准的编译器上正常运行。一方面,正如我刚刚测试的那样,肯定使用 MSVC 10.0。