2011-07-07 6 views
5

私はいくつかのC++のテキストを読み、次のコードを持っています:私は著者はライン1とライン2 にコメントすることにより、意味を理解していないC++の型適合に関する質問?

  1. :私は2つの質問がある

    class A { }; 
    class B : public A { }; 
    
    void main() { 
        A* p1 = new B; // B may be larger than A :OK [Line 1] 
        B* p2 = new A; // B may be larger than A :Not OK [Line 2] 
    } 
    

  2. 2行目でどうしてできないのですか?
+1

実際、基本クラスの空の空きがあり、奇妙なABIレイアウトが原因で、 'A'が' B'よりも大きくなる可能性があります。 –

答えて

8

まあ、ここでは「大きく」は重要ではありません。本当の問題は、「それは」関係です。

class Bの任意のオブジェクトは、(class Bが遺伝に起因もclass Aである)もタイプclass Aであるので、(class Aへのポインタは、ちょうど同様にclass Bのオブジェクトを指すことができる)最初の行は大丈夫ですが、逆であります真ではない(class Aclass Bではなく、class Bの存在も知らないかもしれません)、2行目はコンパイルされません。

0

BがAから誘導されるので、ポインタB *はポインタ・ツー・BはBAよりも多くの情報を持つことを期待することができるので

0

B* p2 = new A;が無効であるA.を指し示すことができません。

例:

class B : public A { 
public: 
    int notInA; 
}; 

B* p2 = new A; 
p2->notInA = 5; // Wait, which notInA are we talking about? 
       // p2 is really an A, and As don't have notInA! 
7

著者が、彼はC++(または一般にプログラミングを)理解していないことを示しています。サイズ(「より大きい」)の問題はありません。問題はB "isA" Aであるため、AへのポインタはBへのポインタで初期化できます。しかし、その逆は真実ではありません。

+0

よく分からない初心者のために泣いているC++の本がたくさんあります。 –

1

コメントは本当に愚かです。オブジェクトのサイズはあまり関係ありません。問題は、暗黙的にアップキャストのポインタ型ができますが、ダウンキャストはできないことです。

BTW、mainは返品タイプがintである必要があります。 Not void

0

ポインタを使用している場合、ポインタ値はメモリ位置です。つまり、メモリ内のANYポイントを指すポインタを設定できます。そのメモリを「逆参照」し、その時点で格納されているオブジェクトを操作するには、にはどのオブジェクトがあるかを知る必要があります。が必要です。
BクラスがAクラスを継承する場合、Bには「A」が含まれます。 Sharptoothが正しいということは、「Is-A」関係があるということです。 "B"は "A"ですが、 "A"は "B"ではありません。
問題は、次のコードで正確に同じようになります:

string* s = new string(""); 
int a = 44; 
s = (string*)&a; //compiler error if not cast 
cout << s; // randomness printed. 
0
class B : public A { }; 

A* p1 = new B; // B may be larger than A :OK [Line 1] 
B* p2 = new A; // B may be larger than A :Not OK [Line 2] 

私は著者の1行目と2行目
にコメントで何を意味するのか理解していないのはなぜ私たちがすることはできません2行目?それが含まれているメンバ変数の観点から - -

class Bはこれ、class Aに由来し、それはAが持っているすべてのもの、それは自分自身を追加することを選択したものを有することを意味します。あなたの単純なコードでは、Bは何も追加していませんが、追加のデータメンバーを持っていれば、格納するのに必要なメモリは明らかにAよりも簡単です。 Aがない仮想メンバ関数を追加すると、コンパイラは、Bの中に、virtualメンバ関数のアドレスをリストした仮想ディスパッチテーブルのアドレスを記録するポインタを追加することが期待できます。コンパイラは、それが気に入ったら埋め込みを自由に追加することもできます。

したがって、一般的なケースでは、派生クラスのサイズは、その基本クラスのサイズである>=です。ここで

A* p1 = new B; // B may be larger than A :OK [Line 1] 

、しかし多くのスペースBが実際に必要とヒープ/フリーストアから割り当てられ、そのメモリのアドレスはp1に格納されています。 BAより大きい場合、それは違いはありません - とにかくそれはどこかにあります - 重要なことはB*A*に格納できることが保証されていることです。ここで

B* p2 = new A; // B may be larger than A :Not OK [Line 2] 

、新しいAはヒープ上に作成されているが、プログラマはそのアドレスのBがあることをコンパイラに指示しようとしています。コンパイラは(強制されない限り)それを信じません - あなたは単にコンパイラの時間エラーを取得します。あなたが(例えば、P2 =(Bの*)コンパイラを強制(新しいA)) to treat the memory address in P2 as if it were an B , then it may later try to access additional data it expects to be part of any B which simply doesn't exist in any A`行う場合:追加のデータメンバー、仮想ディスパッチポインタなどを...ここ

1

誰もが、正しい答えを与えているが、私は、著者はこれら2つのクラスを考えてみましょなど、「大きな」
によって何を意味するのかを指摘したいと思います:

class Animal { 
    public: 
    bool bIsHungry; 
}; 

class Bird : public Animal { 
    public: 
    bool bIsFlying; 
} 

をそれから私が呼ぶとき

Animal* animal = new Bird; // B may be larger than A :OK [Line 1] 

プログラムは変数 "bIsHungry"と変数 "bIsFlying"に合うように十分なスペースを割り当てます。 (あなたは「動物」を型キャストしない限り、しかし、あなたは唯一の「bIsFlyingが」メモリにも「動物」のために予約されていても「bIsHungry」にアクセスすることができるようになります。)

あなたは

Bird* parrot = new Animal; // B may be larger than A :Not OK [Line 2] 

を呼び出すときプログラムは変数 "bIsHungry"に合うだけの十分な領域を割り当てます。しかし、「オウム」の利用者は、

if(parrot->bIsFlying) 
{ //doSomething() 
    ... 
} 

はこれが原因で、「新しい動物」、プログラムAnimalクラスのためにのみ割り当てられた領域、すなわち「bIsHungry」とありましたし、動作しませんのようなコードを記述する場合があります「bIsFlying」に割り当てられたメモリーはありません。コンパイラはすでにそれを「参照」して「不平を言います」、つまりエラーを報告します。

+0

ありがとうございます。 – ipkiss