11

これら2つのクラスがあるとします

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

これらの両方から継承する場合、これらのクラスのメンバー関数の 1 つを他のコンストラクターで呼び出しても問題ありませんか? 例えば

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 内の基本クラス名の出現順序です。
  • 次に、直接基底クラスは、base-specifier-list に表示される宣言順に初期化されます(mem-initializer の順序に関係なく)。
  • 次に、非静的データ メンバーは、クラス定義で宣言された順序で初期化されます (これも mem-initializer の順序に関係なく)。
  • 最後に、コンストラクターの本体が実行されます。

だから交換するなら

public base_implement,
public base_size

public base_size,
public base_implement

すべてが適切に初期化され、プログラムはほとんどの標準準拠のコンパイラで正常に実行されます。1 つには、テストしたばかりの MSVC 10.0 で確実です。

于 2012-07-15T15:07:12.720 に答える