7

$4.11/2 州 -

B「型 cv のメンバーへのポインター」型の右辺値 ( はクラス型) は、「型cvのメンバーへのポインター」 の右辺値に変換できます。ここで、は の派生クラス (節 10) です。がアクセス不能 (節 11)、あいまい (10.2) または仮想 (10.1) の基底クラスである 場合、この変換を必要とするプログラムは形式が正しくありません。TBD TDBBD

B私の質問は、仮想基底クラスではないという制限があるのはなぜDですか?

4

3 に答える 3

5

非仮想基本クラスが関係する状況を考えてみましょう。

class A { int a; }
class B : public A { int b; }
class C : public A { int c; }
class D : public B, public C { int d; }

考えられるメモリ レイアウトは次のとおりです。

+-------------+
| A: int a;   |
+-------------+
| B: int b;   |
+-------------+
| A: int a;   |
+-------------+
| C: int c;   |
+-------------+
| D: int d;   |
+-------------+

DはandAから継承し、両方ともサブオブジェクトを持っているため、最終的に 2 つのサブオブジェクトになります。BCA

メンバー変数へのポインターは、通常、オブジェクトの先頭からの整数オフセットとして実装されます。この場合、オブジェクトの整数オフセットint aAゼロです。したがって、「int a型へのポインタA」は、単にゼロの整数オフセットである可能性があります。

「型へのポインター」を「int a型へのポインター」に変換するには、 (最初のサブオブジェクト) にあるサブオブジェクトへの整数オフセットが必要です。Aint aBABA

「型へのポインター」を「int a型へのポインター」に変換するには、 (2 番目のサブオブジェクト) にあるサブオブジェクトへの整数オフセットが必要です。Aint aCACA

Bコンパイラはと がどこにCあるかを知っているので、コンパイラはからまたはAにダウンキャストする方法について十分な情報を持っています。ABC

ここで、仮想基本クラスが関係する状況を考えてみましょう:

struct A { int a; }
struct B : virtual public A { int b; }
struct C : virtual public A { int c; }
struct D : public B, public C { int d; }

可能なメモリ レイアウト:

+-------------+
| B: ptr to A | ---+
|    int b;   |    |
+-------------+    |
| C: ptr to A | ---+
|    int c;   |    |
+-------------+    |
| D: int d;   |    |
+-------------+    |
| A: int a;   | <--+
+-------------+

仮想基本クラスは通常、BC(から仮想的に派生するA) に単一のサブオブジェクトへのポインタを含めることによって実装されAます。Aサブオブジェクトへのポインタが必要なのは、 とAに対する相対位置が一定Bではないためです。C

int a「型へのポインタ」しかない場合、とサブオブジェクトの位置が に対して異なる可能性があるため、それを「型Aへのポインタ」にキャストすることはできません。にはnorへのバックポインターがないため、ダウンキャストが機能するのに十分な情報がありません。int aBBCAABC

于 2010-08-17T12:19:44.700 に答える
1

非仮想継承を使用すると、基本クラスと派生クラスのメンバーを、基本クラスを最初にメモリ内に連続して配置できるため、各基本クラスのメンバーは、オブジェクトのアドレスを基準にして同じ場所に配置されます。BまたはですD。これにより、pointer-to-member-of-Bを pointer -to-member-of- に簡単に変換できますD。どちらも、オブジェクトのアドレスからのオフセットとして表すことができます。

仮想継承では、派生オブジェクト内のポインター (または同等のもの) を介して基本クラスのメンバーにアクセスし、基本クラスの場所を示す必要があります。これには、この間接化が必要であることを示すために、メンバーへのポインター表現に追加情報を追加する必要があり、メンバーへのポインターを使用する場合は実行時チェックが必要になります

多くの C++ の背後にある一般原則は、可能な限り実行時のオーバーヘッドを回避することです。この場合、非常に一般的な操作に対する実行時チェックと、非常にあいまいな変換を許可しないという選択がありましたが、そのプリンシパルがここで適用されたようです。

于 2010-08-17T12:20:17.017 に答える
0

本当に興味深い質問です。今日新しいことを学びました。これは、件名に関連して私が見つけたものです: 派生クラスから仮想基底クラスへのメンバー関数ポインターのキャストは機能しません

于 2010-08-17T12:17:48.053 に答える