4

これに関していくつか質問がありますが、私はまだこれについてはっきりしていません。この多重継承を考えてみましょう。

class Base1 
{ 
   public: 
     Base1(); 
     virtual ~Base1(); 
     virtual void speakClearly(); 
     virtual Base1 *clone() const; 
   protected: 
     float data_Base1; 
}; 

class Base2 
{ 
   public: 
     Base2(); 
     virtual ~Base2(); 
     virtual void mumble(); 
     virtual Base2 *clone() const; 
   protected: 
     float data_Base2; 
}; 

class Derived : public Base1, public Base2 
{ 
   public: 
     Derived(); 
     virtual ~Derived(); 
     virtual Derived *clone() const; 
   protected: 
     float data_Derived; 
}; 

そして、これらの2つのステートメントを検討してください

Base1 *pbase1 = new Derived; 
Base2 *pbase2 = new Derived;

C++ オブジェクト モデル内の本には、最適化の 1 つに記載されています。

pbase1 サブオブジェクトの vtable と Derived は同じ vtable を共有します。本の正確なフレーズ

"基数 1 が左端で、既に派生クラス オブジェクトの先頭を指し"

これはどのように起こりますか?継承の順序を変更すると、これが変わることを理解しています

class Derived : public Base2, public Base1

しかし、これがコンパイラによってどのように行われるかわかりません。pbase1 と派生が同じ v_table を共有する方法を誰か説明できますか?

4

2 に答える 2

2

それはすべて非常に実装で定義されていますが、通常、コンパイラは次のようにレイアウトしDerivedます

Base1: vptr
Base1: data
Base2: vptr
Base2: data
Derived: data

のアドレスはDerived全体のアドレスであるため、 の物理アドレスと同じですBase1。の vtable の最初の部分は のDerivedそれと同じで Base1、Base1: vptr は実際には の vtable を指し Derivedます。

Base1と宣言を逆Base2にすると、コンパイラは上記の役割を逆にします。(通常、標準には、コンパイラがベースをアルファベット順に並べたり、派生データを最初に配置したりすることを妨げるものは何もありませんが、そうするコンパイラについては聞いたことがありません。)

于 2013-10-02T08:27:05.260 に答える
1

クラスには、宣言したメンバーの前にいくつかの「隠しメンバー」があると考えてください。次のようなレイアウトが得られます。

  • vtable ポインター
  • 最初の基地
    • vtableポインタです
    • 自分のメンバーです
  • セカンドベース
    • vtableポインタです
    • 自分のメンバーです
  • 派生メンバー

ここでの問題は、「ポインタはどこを指すべきか」です。

すべての関数は最も派生したオブジェクトで使用できるため、次の目的で「関数ポインターの配列」を作成できます。

  • ドクター、
  • はっきりと話します
  • クローン
  • つぶやく

この表は、Derivedと(最後の行を無視するだけでよい) でも同じように機能しますBase1が、 では機能しません。Base2

  • dtor
  • クローン
  • つぶやく

以前とは異なるインデックスを使用します (そのため、異なる必要があります)。

この事実のため、Derived テーブル インスタンスと最初のベース テーブル インスタンスを区別する必要はありません (もちろん、最後のベースから始めて前のものを区別する同じ推論を行うことができます ... しかし、どこかから始めなければなりません)。

したがって、コンパイラは、最も派生した仮想関数へのすべてのポインターを初期化するよりも、最初の基本仮想関数と他のすべての関数を含むテーブルと、他の基本の他の個別のテーブルを作成することにより、実装を「圧縮」します。機能。

レイアウトは

  1. 派生 vtable
  2. Base1 メンバー
  3. Base2 と派生 vtable
  4. Base2 メンバー
  5. 派生メンバー

次に、new Derivedそのスケッチを作成します。

  • toDerived*を指定すると、1 を指すようになります。
  • If given to Base1*will make it point to 1. (Derived と同じですが、「短い」)
  • toBase2*を指定すると、3 を指すようになります。
于 2013-10-02T08:41:35.630 に答える