0

わかりました、これは私をとても混乱させる簡単な例です...

class First { public: void Func() {cout<<"FirstFunc";} };
class Second : public First
    { public: void Func() {cout<<"SecondFunc";} };
class Third : public Second
    { public: void Func() {cout<<"ThirdFunc";} };

int main () {
   Third * thirdptr = new Third();
   Second * secondptr = thirdptr;
   First * firstptr = secondptr;

   firstptr->Func();  // prints out FirstFunc
   secondptr->Func();  // prints out SecondFunc
   thirdptr->Func();  // prints out ThirdFunc

   delete thirdptr;

そして、これは仮想関数を使用しています

class First { public: virtual void Func() {cout<<"FirstFunc";} };
class Second : public First
    { public: virtual void Func() {cout<<"SecondFunc";} };
class Third : public Second
    { public: virtual void Func() {cout<<"ThirdFunc";} };

int main () {
   Third * thirdptr = new Third();
   Second * secondptr = thirdptr;
   First * firstptr = secondptr;

   firstptr->Func();  // prints out ThirdFunc
   secondptr->Func();  // prints out ThirdFunc
   thirdptr->Func();  // prints out ThirdFunc

   delete thirdptr;

わかりました、これが私の質問です。

  1. Third * thirdptr = new Third(); の読み方 new は、「new int」のときに int にメモリを割り当てますが、new Third(); をどのように読み取るべきかわかりません。コンストラクタなので

  2. Second * secondptr = thirdptr; / 最初 * firstptr = secondptr; これらの 2 つのステートメントは非常に紛らわしいです。& 演算子またはアドレス演算子に関連する簡単な言葉で説明できる人はいますか? このポインタと継承の概念は理解できましたが、この部分が非常にややこしいです。

  3. 2 番目の例の結果を取得するにはどうすればよいですか? 私が読んでいる本は言っている

最初の例 // ポインター算術演算に関する C++ コンパイラーは、 // ポインターが実際に何を指しているかではなく、ポインターの型に基づいて決定を行います

2 番目の例 // VIRTUAL FUNCTION : ポインターの型に基づいてではなく、 // ポインターが実際に何を指しているかに基づいて何を呼び出すかを決定します

これは翻訳なので、正確ではないかもしれませんが、それでも理解できません。あなたが私を助けることができれば、私は本当に感謝します!

4

2 に答える 2

0

最初の質問に答えるにnew Third()は、高レベルで と同じことを行うことnew intです。あなたが言ったように、new intは にメモリを割り当てint、左側のポインタはそのメモリを指します。 new Third()オブジェクトにメモリを割り当て、クラスThirdのデフォルト コンストラクタ を使用してオブジェクトを初期化します。ThirdThird()

2 番目の質問では、3 つのポインターを作成し、それらすべてを同じメモリ ブロックにポイントしています。継承のおかげで、各ポインター型を上位の型に暗黙的にキャストすることが可能であると思います (static_castまたは C スタイルのキャストなどの明示的な指示のない暗黙的なキャストの意味)。視覚的に:

----
|  | <-- Some block of memory.
----

A -> ---- <-- A* aptr = new A();
     |  |
     ----

A -> ----
B -> |  | <-- B* bptr = aptr;
     ----

A -> ----
B -> |  |
C -> ---- <-- C* cptr = bptr;

3 つのポインタすべてが同じメモリ ブロックを指していることに注意してください。(また、すべてのポインタがメモリの先頭を指していることにも注意してください- まったく同じ場所です。上でそれを表現するのは困難です。)

3番目の質問について:

ポインターの型に基づいてではなく、ポインターが実際に指しているものに基づいて何を呼び出すかを決定する

つまり、メモリを指しているポインタの型ではなく、メモリ内のオブジェクトの実際の型に基づいて、関数のどのインスタンスを呼び出すかをコンパイラが決定します。したがって、あなたの例では、タイプのオブジェクトを作成し、それをThird指す 3 つのポインターを持っています。ただし、ポインターの型に関係なく、は仮想であるため、メモリ内のオブジェクトの型が であるため、のバージョンが実行されます。FirstSecondThirdFuncThirdFuncThird

于 2013-03-21T03:39:27.817 に答える
0
  1. new Third()は 2 つのことを行います。最初にThirdobject にメモリを割り当て、次にコンストラクタを呼び出しますThirdnew Thirdステートメントは、メモリの割り当てと()コンストラクタの呼び出しの2 つの部分に分かれていると考えることができます。

  2. Second * secondptr = thirdptr; / First * firstptr = secondptr; 3 つの変数はすべて同じポインターに割り当てられます。thritptrそれが 100に等しいとしましょう。これらのステートメントの最後では、 と のsecondptr両方が 100 になります。firstptr3 つのポインターはすべて、メモリー内の同じアドレスを指しています。ただし、それらは異なる型であるため、コンパイラーはそれらを異なるものとして反復します

  3. 非仮想関数の場合、変数の型に基づいて、コンパイル時にメソッド呼び出しが解決されます。そのため、最初のコードをコンパイルするfirstptrFirst*、コンパイラは への呼び出しを生成しますFirst::Func()。仮想関数の場合、メソッド呼び出しは実行時まで完全には解決されません。コンパイラは、変数が指す実際の型を決定し、その関数を呼び出すコードを生成します。2 番目の例では、プログラムを実行すると、firstpr->Func()最初に が実際にオブジェクトをfirstptr指していると判断され、 が呼び出されます。これは通常、仮想関数テーブルを使用して行われます。ThrirdThird::Func()

于 2013-03-21T03:35:56.023 に答える