3

申し訳ありませんが、質問をより良い方法で書き直すべきかもしれません。

ABC関数名を持つ基本クラス名があります。

void saysez(ostream &os) const // this is NOT virtual!!
            { os << sez; }

DEF関数名も持つ派生クラス名

void saysez(ostream &os) const { os << extra << " ";
                         scary::saysez(os);

上記のコードからわかるように、両方とも同じ署名を持っています。

私の理解では、virtualキーワードが指定されていない場合は基本クラス関数を使用する必要がありますが、チュートリアルの実践では、派生関数を使用していることが判明しました。

では、なぜベースの関数よりも派生の関数が使用されるのか疑問に思っていますか?

以下は、からの呼び出しですint main

     w.saysez(cout);  cout << '\n';

w派生クラスのオブジェクトです。

以下は、出力付きの切り取られたコードへのリンクです

http://codepad.org/Pz5jwMVP

4

4 に答える 4

2

あなたの質問を理解するのに苦労しています。virtual キーワードは、この問題とは無関係のようです。クラス witch にはメンバー関数 void sayez(ostream&) があり、このクラスのインスタンスを作成して w.saysez を呼び出すと、コンパイラは witch の実装で見つかった sayez の定義と一致します。

ここでは virtual キーワードは重要ではありません。「Double Double」の部分を見たくない場合は、 w を怖いものにキャストします。

((scary*)&w)->saysez(cout)

「Double Double」の部分は印刷されません。

詳細については、これを参照するか、仮想キーワードについて説明している他のサイトを参照してください。

于 2012-04-17T00:22:27.717 に答える
1

w は witch 型の要素として宣言されていると思いますので、もちろん witch::saysez() を呼び出します。ただし、次のことを行った場合:

scary* w2 = new witch();
w2->saysez(cout);

...すると、怖い::saysez() の動作が見られるはずです。

非仮想関数が継承されたクラスでどのように機能するかを誤解しているようです。仮想関数と非仮想関数の両方をオーバーライドできます。クラス Foo のオブジェクトで Foo::MyNonVirtualFn() を呼び出すと、Foo::MyNonVirtualFn() の本体のみが実行されます。Foo に MyNonVirtualFn() を実装するスーパークラスがある場合、またはそれが呼び出されているオブジェクトが、実際には MyNonVirtualFn() をオーバーライドする Foo のサブクラスのインスタンスである場合、それは問題ではありません。関数呼び出しが Foo::MyNonVirtualFn() に対するものである場合、それが実行される関数のバージョンです。(MyNonVirtualFn() のどのバージョンを呼び出しているか明示していない場合は、それを呼び出している型から推測します。ポインターまたは参照型の場合、これは物体。)

オブジェクトで Foo::MyVirtualFn() を呼び出すと、そのオブジェクトが MyVirtualFn() をオーバーライドした Foo のサブクラスのインスタンスであるかどうかがチェックされるという点で、仮想関数は異なります。たとえば、次のようになります。

Base* base = new Base();
Derived* derived = new Derived();
Base* derived_as_base = derived;

base->MyNonVirtualFn();  // Calls Base::MyNonVirtualFn().
derived->MyNonVirtualFn();  // Calls Derived::MyNonVirtualFn().
derived_as_base->MyNonVirtualFn();  // Calls Base::MyNonVirtualFn() because it's called on a Base*.

base->MyVirtualFn();  // Calls Base::MyNonVirtualFn().
derived->MyVirtualFn();  // Calls Derived::MyNonVirtualFn().
derived_as_base->MyVirtualFn();  // Calls Derived::MyNonVirtualFn() because it uses a lookup table.
于 2012-04-17T00:14:45.783 に答える
1

これは、まだ問題を示している投稿可能な小さなサンプルです。

#include <iostream>
using namespace std;
#include <cstring>

class scary {
    char is[31];
    char sez[31];
public:
    scary() {
        strcpy(is, "Creep");
        strcpy(sez, "boooo");
    }
    scary(const char i[], const char s[])
    {
        strcpy(is, i); strcpy(sez, s);
    }
    virtual void sayis(ostream &os) const { os << is; }
    void saysez(ostream &os) const // this is NOT virtual!!
        { os << sez; }
};

ostream &operator<<(ostream &os, const scary &x) {
    x.saysez(os);
    os << ", said the ";
    x.sayis(os);
    return os;
}

class ghost: public scary {
public:
    ghost():scary("Ghost", "Boo!")
    {
    }
};

class witch: public scary {
    char extra[31];
public:
    witch(): scary("Witch", "Toil and Trouble") {
        strcpy(extra, "Double, Double");
    }
    void saysez(ostream &os) const {
        os << extra << " ";
        scary::saysez(os);
    }
};

int main() {
    scary s; 
    ghost g; 
    witch w;
    cout << s << '\n' << g << '\n' << w << '\n';
    return 0;
}

出力:

boooo, said the Creep
Boo!, said the Ghost
Toil and Trouble, said the Witch

witchのコンストラクターで、配列を に設定しsez"Toil and Trouble"次にsaysez宣言された で出力し、出力するの関数を呼び出しwitchます。これは、非仮想メンバー関数をオーバーライドできる (推奨されません) ためです。extrascarysaysezsez

于 2012-04-17T00:11:57.567 に答える
1

まず、質問を正しく理解していない場合は修正してください。

多くの理由で継承を使用しますが、そのうちの 1 つは効率的にコーディングすることです。たとえば、あなたの場合、

怖いクラスを継承したいということは、派生クラスで怖いクラスの機能を持ちたいということです。ベース クラスのすべてのメンバー関数をコピーして、ベース コールと同じ機能を持たせることができますが、関数の 1 つで何かを変更する場合はどうでしょうか。それらすべてを変更する必要があります。したがって、非効率的なコーディング方法です。

クラスでは、なぜこの行にオブジェクト w があるのか​​ を尋ねますw.saysez(cout); cout << '\n';。同じメンバー関数を持つ怖いクラスとクラスの両方が、w最初にそのクラスで一致を探す必要があるためです。見つからない場合は、次に検索しますベースクラスで。したがって、オブジェクトはsaysezそのクラスでメンバー関数を呼び出しています。

これが役立つことを願っています。

于 2012-04-17T00:25:37.473 に答える