7

参照コード:

#include <vector>
#include <iostream>

class Func {
public:
    virtual void call() {
        std::cout<< "Func -> call()" << std::endl;
    }
};

class Foo : public Func {
public:
    void call() {
        std::cout<< "Foo -> call()" << std::endl;
    }
};

class Bar : public Func {
public:
    void call() {
        std::cout<< "Bar -> call()" << std::endl;
    }
};

int main(int argc, char** argv) {
    std::vector<Func> functors;

    functors.push_back( Func() );
    functors.push_back( Foo() );
    functors.push_back( Bar() );

    std::vector<Func>::iterator iter;
    for (iter = functors.begin(); iter != functors.end(); ++iter)
        (*iter).call();
}

そのコードを実行すると、コンピューターで次の出力が生成されます。

$ ./test
Func -> call()
Func -> call()
Func -> call()

このインスタンスで正しい仮想関数が呼び出されるようにする方法はありますか? 私はC++が初めてですが、私の最善の推測は次のとおりです。

(*iter).call();

オブジェクトにキャストされていFuncます。これは正しいです?

4

7 に答える 7

7

ポリモーフィック型のコレクションの要素を保持するには、shared_ptr または unique_ptr を使用する必要があります。

あなたのコードが書かれているので、Foo と Bar のインスタンスは、ベクトルに収まるように Func 型のインスタンスに強制 (コピー構築) されます。(その理由は、ベクトルはパフォーマンスのために固定サイズの値でその要素をすぐに保存するためですが、ポリモーフィック サブクラスはコンパイル時に不明な任意の大きなサイズであるため、基本クラスのみを保存できます。)

これの方が良い:

int main(int argc, char** argv) {
    vector<shared_ptr<Func>> functors;

    functors.push_back( make_shared<Func>() );
    functors.push_back( make_shared<Foo>() );
    functors.push_back( make_shared<Bar>() );

    for (auto functor : functors)
        functor->call();
}

上記では、参照カウント ポインターを使用して、ベクトル内の Func の異種サブクラスを暗黙的に共有しています。(この間接化により、Func の任意のサイズのサブクラスをアドレス間接化によって格納できます。)

また、独自のファンクター型をローリングするのではなく、std::function と std::bind を確認することもできます。

注目すべきもう 1 つの点は、完全な転送と varadic テンプレートです。

update : 古いコンパイラの場合:

int main(int argc, char** argv) {
    vector<std::tr1::shared_ptr<Func> > functors;

    functors.push_back( std::tr1::make_shared<Func>() );
    functors.push_back( std::tr1::make_shared<Foo>() );
    functors.push_back( std::tr1::make_shared<Bar>() );

    for (size_t i = 0; i < functors.size(); ++i)
        functors[i]->call();
}
于 2012-04-14T15:57:04.220 に答える
3

ベクトルはFunc値によってのみ型を保持します。つまり、すべてのテンポラルFooBarスライスされ、基本Func型にキャストされます。

std::vector< Func* >ポリモーフィックディスパッチが機能するためには、次のようなものに変更し、派生クラスのインスタンスを動的に割り当てる必要があります

この関数が戻った後にこのベクトルを他の関数に渡さないことが完全に確実な場合は、最適化としてスタックにインスタンスを割り当てることができます。

std::vector< Func* > v;
Bar b;
Foo f;
v.push_back( &b);
v.push_back( &f);
于 2012-04-14T15:57:21.613 に答える
1

一般に、サブクラスのインスタンスはそのスーパークラスのインスタンスよりも大きい可能性があるため、サブクラスがベクターのスロットに収まるとは思わないでください。

そして、push_backおそらく内部的にコピーコンストラクターを呼び出します(Funcあなたが持っているので、クラスのvector<Func>)ので、内部ベクトルスロットは実際にFuncは他のクラスのものではありません。

于 2012-04-14T15:59:49.280 に答える
1

あなたの std::vector は Func オブジェクトを格納しています - これは、呼び出すときに

functors.push_back( Foo() );
functors.push_back( Bar() );

オブジェクトを作成FooしてからBar、それらのオブジェクトをオブジェクトにコピーするときに「スライス」しFuncます。

Foo と Bar をポリモーフィックに使用したい場合、より典型的なパターンは、いくつかのポインター型のベクトルを格納することです (ただし、「生の」ポインターではないことが望ましい)。たとえば、

std::vector< std::unique_ptr<Func> >

std::vector< std::shared_ptr<Func> >

または、本当に必要な場合.. (ただし、shared_ptr または unique_ptr を持たない古いコンパイラを使用している場合のみ)

std::vector< Func* >

于 2012-04-14T15:57:19.410 に答える
1

C++ では、ポリモーフィズムはポインターと参照でのみ機能しますが、ベクトルはオブジェクトのインスタンスを直接格納します。push_backのコピー コンストラクターを呼び出すと、ベクター内に格納されているオブジェクトがFunc構築されます。Func

これはオブジェクト スライシングと呼ばれ、StackOverflow で簡単に検索して詳細を確認できます。

解決策は、別の場所 (おそらくヒープ上) に割り当てる必要があるオブジェクトへのポインター (または、より適切にはスマートポインター) を格納することです。

于 2012-04-14T15:58:00.433 に答える
0

あなたの問題は、のベクトルがあることですが、Funcメソッドは参照またはポインターを介してのみ多態的に呼び出されます。

于 2012-04-14T15:59:28.340 に答える