5

この回答が既にこのサイトにある場合は申し訳ありませんが、私はすべてを調べましたが、問題の解決策が見つかりませんでした. 継承されたクラスからの同名関数に関係します。これが私のコードです:

class A
{
public:
    int foo(int c) { c = c+1; return c; };
};

class B : public A
{
public:
    int foo(int c) { c = c-1; return c; };
};

int main()
{
    A array[2];
    array[0] = A item1;
    array[1] = B item2;
    for (int n=0;n<2;n++)
    {
        cout << array[n].foo(10) << endl;
    }
    return 0;
}

次の出力が期待されます。

11    // foo() from A class  [10 + 1 = 11]
9     // foo() from B class  [10 - 1 = 9 ]

しかし、代わりに私は得る

11
11

これをテストしたところ、B クラスの foo() 関数が for ループ内で呼び出されないことがわかりました。代わりに、A クラスの foo() 関数が、配列 [1] の B オブジェクトでも呼び出されます。

これは、A クラスのオブジェクトのみを含むように配列を定義したためですか? もしそうなら、B クラスの foo() 関数をその for ループ内の 2 番目のオブジェクトで呼び出す方法はありますか?

助けてくれてありがとう!

4

3 に答える 3

9

有効な C++ ではないことを忘れて、型のオブジェクトをに割り当て、型のオブジェクトarray[0] = A item1;を に割り当てていると仮定します。さて、あなたには2つの問題があります。Aarray[0]Barray[1]

1 つ目は、オブジェクトのスライスとして知られています。type のオブジェクトを typeBのオブジェクトにコピーすると、そのオブジェクトの一部Aだけがコピーされます。Aだからあなたが持っているのはまったくでarray[1]はなく、BただのA. ポリモーフィズムが必要な場合 (そうします)、ポリモーフィックな動作を提供するポインターまたは参照のいずれかを使用する必要があります。つまり、配列を anA* array[2];にして do にするということですarray[0] = &item1; array[1] = &item2;

これで、ポインターを指すポインターで関数を呼び出すと、Aメンバー関数Bのみが呼び出され Aます。fooなんで?デフォルトでは、関数はオブジェクトの静的タイプで検索されるためです。その静的タイプはA. オブジェクトの動的な型(オブジェクトの真の型) で関数を検索するようにコンパイラに指示する場合はB、そのメンバー関数を仮想にする必要があります。では、A次のようにします。

virtual int foo(int c) { c = c+1; return c; };

コンパイラが を呼び出しfooているA*ことを確認すると、それが仮想であることを認識し、「オーケー、この関数を動的に検索する必要があります」と言って、Bの実装を見つけますfoo

于 2012-12-04T21:06:39.123 に答える
6

Bタイプに割り当てられたスペースにインスタンスを割り当てていますA。これが「スライス」につながります。

そのため、最初に、ポインターまたは参照を介して他の型を許可する必要があります。例えば:

A* a = new B;
a->foo(10);

あなたがしなければならないもう一つのことは、foo()オーバーライドされる可能性があることをコンパイラに警告することです。C++ では、virtual関数として宣言します。

virtual int foo(int c) { c = c+1; return c; };
于 2012-12-04T21:05:08.273 に答える
4

virtualキーワードがありません。

 class A
 {
   virtual int foo(int c) { c = c+1; return c; };
 };

また、スライスを避けるために、配列の代わりにAポインタの配列を使用しますA

A* array[2];
array[0] = new A;
array[1] = new B;
for (size_t i=0; i<2; ++i)
    cout << array[i]->foo(10) << endl;

または、さらに良いのは、それを A へのポインターのベクトルにすることです ( std::vector<A*>)。

また、経験則として、基本クラスのデストラクタも仮想にする必要があります。この特定の例では問題ありませんが、派生クラスが破棄時に解放する必要がある新しいメンバーを追加する場合、基本クラスのデストラクタを仮想にすることで確実に階層内のすべてのデストラクタが呼び出されます。

別のこと、これ:

array[1] = B item2;

コンパイルされません。配列を A へのポインターの配列 (または A へのポインターのベクトル) として宣言すると、次のことが可能になります。

array[1] = new B;
于 2012-12-04T20:59:45.830 に答える