11

假设我有这两个类

class base_size
{
public:
   int size()
   { return 5; }
};

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())
   {
      // Is this OK? 
      // If derived is not yet constructed can I access this->size() ?
      // Works in VC++. Not sure about other compilers.
   }
};
4

2 回答 2

8

原则上没问题。基子对象和成员对象是在派生构造函数体运行之前构造的,因此您可以毫无问题地调用成员函数。你甚至可以在构造函数中调用自己的成员函数;您只需要确保它们不依赖于稍后在同一个构造函数中出现的任何内容。

只需确保以正确的顺序调用基本初始化程序,即它们的声明顺序,和/或修复您的类定义:因为base_size::size()您希望base_size完全构造子对象,所以它必须首先出现。

 class derived : base_size, base_implement
 {
     derived() : base_size(), base_implement(size()) { /* ... */ }
     // ...
 };
于 2012-07-15T15:04:33.593 回答
3

您可以安全地从初始化列表中调用继承的成员函数,前提是该函数所做的任何事情都与该继承级别的成员数据的初始化方式无关。而且,因为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。

于 2012-07-15T15:07:12.720 回答