1

BruceEckelによるThinkinginC++を読んでいます。第15章(第1巻)の「コンストラクター内の仮想関数の動作」という見出しの下で、彼は次のように述べています。

コンストラクター内にいて、仮想関数を呼び出すとどうなりますか?通常のメンバー関数の内部では、何が起こるかを想像できます。オブジェクトは、メンバー関数が属するクラスに属しているのか、それから派生したクラスに属しているのかをオブジェクトが認識できないため、実行時に仮想呼び出しが解決されます。一貫性を保つために、これがコンストラクター内で行われるべきことだと思うかもしれません。

ここでブルースは、オブジェクトのコンストラクター内で仮想関数を呼び出すと、ポリモーフィズムは表示されない、つまり現在のクラスの関数が呼び出されるだけで、その関数の他の派生クラスバージョンではないことを説明しようとしています。これは有効であり、理解できます。クラスのコンストラクターは、クラスが実行されているのか、他の派生オブジェクトの作成のために実行されているのかを事前に知らないからです。さらに、そうすると、部分的に作成されたオブジェクトで関数が呼び出されることになり、悲惨なことになります。

彼が通常のメンバー関数について述べている最初の文のために私の混乱が突然起こりましたが、彼は仮想呼び出しが実行時に解決されると言っています。しかし、待ってください。クラスのメンバー関数内で、別の関数(仮想または非仮想)を呼び出すと、それ自体のクラスバージョンのみが呼び出されます。例えば

class A
{
    virtual void add() { subadd(); }
    virtual subadd() { std::cout << "A::subadd()\n"; }
};

class B : public A
{
    void add() { subadd(); }
    void subadd() { std::cout << "B::subadd()\n"; }
};

上記のコードでは、A::add()への呼び出しsubadd()が行われると、常に呼び出しが行われ、A::subadd()同じことが当てはまりますBよね?では、「仮想呼び出しは、メンバー関数が属するクラスに属しているのか、それから派生したクラスに属しているのかをオブジェクトが認識できないため、実行時に解決される」とはどういう意味ですか?

彼は基本クラスポインタを介した呼び出しに関してそれを説明していますか?(私は本当にそう思う)その場合、彼は「通常のメンバー関数の内部」と書くべきではない。これまでの私の理解では、同じクラスの別のメンバー関数内からのメンバー関数の呼び出しはポリモーフィックではありません。間違っている場合は修正してください。

4

3 に答える 3

7

あなたは間違っています-さらに派生したクラスが仮想関数の一部をオーバーライドする可能性があります。つまり、静的呼び出しが間違っているということです。したがって、例を拡張するには、次のようにします。

class C : public B
{
public:
    // Not overriding B::add.
    void subadd() { std::cout << "C::subadd\n"; }
};

A *a = new C;
a->add();

これは動的にを呼び出しB::add、次に動的にを呼び出しますC::subadd。動的タイプは関数であり、関数をオーバーライドするため、への静的呼び出しB::subaddは間違っています。CC

A::addあなたの例では、 asの複製B::addは不要です。どちらもsubadd、オブジェクトの動的タイプが何であれ、多形的に呼び出します。

于 2010-03-02T13:41:22.680 に答える
3

別の関数(仮想または非仮想)を呼び出すと、それ自体のクラスバージョンのみが呼び出されますよね?

申し訳ありませんが、間違いました。コンストラクターは例外です。それ以外の場合は、完全なポリモーフィズムが設定されている完全に構築されたオブジェクトを処理しています。後の例(BがAを継承している場合)では、(BがAから継承している場合)と呼ばれますB::subadd()(テストドライブのコードを取得するだけです。これは、プログラミング言語がどのように機能するかを学ぶための最良の方法です)。

于 2010-03-02T13:31:21.013 に答える
1

あなたの例の呼び出しsubadd();は、実際には呼び出しthis->subadd();です。ではA::add()、のタイプはthisですがA*、オブジェクトthisを指している可能性がありCます(CもAから派生していると仮定します)。をコンパイルするときA::add()、コンパイラはどの仮想関数がでオーバーライドされているかを知ることができませんclass C。コール this->subadd();インA::add()は実際にを呼び出す場合がありますC::subadd()。したがって、コンパイラは仮想呼び出しを行う以外に選択肢はなく、実行時にどこthis->を指しているかがわかっているときに解決されます。

于 2010-03-02T13:40:40.933 に答える