2

非ポリモーフィック継承にはこのポインター調整が必要ですか? 私が見たすべてのケースで、このポインタ調整は、使用された例がキーワードによるポリモーフィック継承を含むことを議論しましたvirtual

非ポリモーフィックな継承がこのポインタ調整を必要とするかどうかは私には明らかではありません。

非常に単純な例は次のとおりです。

struct Base1 {
    void b1() {}
};

struct Base2 {
    void b2() {}
};

struct Derived : public Base1, Base2 {
    void derived() {}
};

次の関数呼び出しでは、このポインターの調整が必要ですか?

Derived d;
d.b2();

この場合、データ メンバーがアクセスされないため、 this ポインターの調整は明らかに不要です。一方、継承された関数がデータ メンバーにアクセスする場合は、このポインターの調整が適切な場合があります。一方、メンバー関数がインライン化されていない場合は、このポインターの調整がどうしても必要なようです。

これは実装の詳細であり、C++ 標準の一部ではないことは理解していますが、これは実際のコンパイラがどのように動作するかについての問題です。これが、すべてのコンパイラが同じ一般的な戦略に従う vtables のようなケースなのか、それとも非常にコンパイラに依存する質問をしたのかはわかりません。コンパイラに大きく依存している場合は、それ自体で十分な答えになります。または、必要に応じて、gcc または clang に焦点を当てることができます。

4

3 に答える 3

4

オブジェクトのレイアウトは言語によって指定されていません。C++ ドラフト標準 N3337 から:

10 派生クラス

5 基本クラスのサブオブジェクトが最も派生したオブジェクト (1.8) に割り当てられる順序は規定されていません。[注:派生クラスとその基本クラスのサブオブジェクトは、有向非巡回グラフ (DAG) で表すことができます。矢印は「直接派生」を意味します。サブオブジェクトの DAG は、「サブオブジェクト ラティス」と呼ばれることがよくあります。</p>

ここに画像の説明を入力

6 矢印はメモリ内に物理表現を持っている必要はありません。—終わりのメモ]

あなたの質問に来ます:

次の関数呼び出しでは、このポインターの調整が必要ですか?

これは、オブジェクト レイアウトがコンパイラによってどのように作成されるかによって異なります。そうかもしれないし、そうでないかもしれない。

あなたの場合、クラスにメンバー データがなく、仮想メンバー関数がなく、最初の基本クラスのメンバー関数を使用しているため、おそらくポインターの調整は表示されません。ただし、メンバー データを追加し、2 番目の基本クラスのメンバー関数を使用すると、ポインターの調整が発生する可能性が高くなります。

コードの例と、コードを実行したときの出力を次に示します。

#include <iostream>

struct Base1 {
   void b1()
   {
      std::cout << (void*)this << std::endl;
   }
   int x;
};

struct Base2 {
   void b2()
   {
      std::cout << (void*)this << std::endl;
   }
   int y;
};

struct Derived : public Base1, public Base2 {
   void derived() {}
};

int main()
{
   Derived d;
   d.b1();
   d.b2();
   return 0;
}

出力:

0x28ac28
0x28ac2c
于 2015-02-06T19:01:05.827 に答える
1

これは、コンパイラ固有だけでなく、最適化レベル固有でもあります。経験則として、すべてのthisポインターは調整されますが、多くのコンパイラーの例のように 0 になる場合があります (ただし、すべてではありません — IIRC、MSVC は注目すべき例外です)。関数がインライン化されていて にアクセスしない場合this、調整は完全に最適化される可能性があります。

于 2015-02-06T18:46:37.733 に答える