1

私はこのような2つのクラスを持っています(簡略化され、名前が変更されました):

class Base
{
 public:
  virtual void operator()( ) { cout << "Base\n";}
};

class Derived : public Base
{
 public:
  void operator()( ) { cout << "Derived\n"; }
};

void doSomething( Base obj )
{
  obj( );
}

int main( )
{
  list<Base> bList;
  Derived d1, d2, d3;
  bList.push_back( d1 );
  bList.push_back( d2 );
  bList.push_back( d3 );

  for( list<Base>::iterator it = bList.begin(); it != bList.end(); it++ )
    {
      doSomething( *it );
    }
  return 0;
}

これを実行すると、次のようになります。

Base
Base
Base

つまり、Base :: operator()が呼び出されます。明らかに、これは私がしたいことではありません。doSomethingをに変更してみました

void doSomething( Base& obj )

ただし、これはDerivedオブジェクトで直接呼び出された場合にのみ機能し、リストのオブジェクトでは機能しません。また、参照をリストに保存することはできません。
doSomethingを(ポインターに頼る必要なしに)派生クラスの関数を呼び出さなければならないことを「見る」ようにする方法はありますか?

編集:問題が指摘されています、これに遭遇するかもしれない他の人々のためにここにリンクがあります: オブジェクトスライスとは何ですか?

4

3 に答える 3

7

以下:

list<Base> bList;
Derived d1, d2, d3;
bList.push_back( d1 );
bList.push_back( d2 );
bList.push_back( d3 );

オブジェクトをスライスします。それらはもはやタイプDerivedではありませんが、Base。期待される動作を実現するには、コンテナーでポインターまたはスマートポインターを使用する必要があります。

オブジェクトのスライスについて読んでください。

于 2012-04-29T14:51:19.313 に答える
2

問題は、コンパイラが派生クラスの関数を呼び出す必要があることを「認識」しないことではありません。問題は、基本クラスのみをコンテナに格納していることですlist<Base>。これにより、一般にオブジェクトスライスと呼ばれるものが作成されます。Derivedコンパイラがオブジェクトを保持できるコンテナにオブジェクトをコピーする場合、コンパイラBaseはオブジェクトのベース部分のみをコピーし、オブジェクトを作成する部分を「スライス」することができDerivedます。

値のセマンティクスに従うコンテナーでポリモーフィズムを利用するには(つまり、オブジェクトへの参照の形式ではなく、実際のオブジェクトをコンテナーに格納すると想定します)、そのオブジェクトへのポインターを格納する必要があります。生のポインタを標準ライブラリコンテナに保存しないことをお勧めします。これは、追加のライフサイクル管理の問題を引き起こすためです。コンテナの外部からポイントされたオブジェクトのライフタイムを制御する必要があります。これは非常に悪い考えであり、通常はどちらかです。両方ではないにしても、メモリリークまたはダングリングポインタにつながります。

Boostを使用できる場合は、ptr_containerこの目的のために明示的に設計されたテンプレートクラスの1つを使用することをお勧めします。それができず、十分に最新のコンパイラを使用している場合は、を使用してstd::list<std::shared_ptr<Base> > >、に依存しshared_ptrてオブジェクトのライフサイクルを管理できます。

于 2012-04-29T14:59:52.290 に答える
0

上記の答えは私を究極の解決策に導きます。簡単な答えは、「いいえ。ポインタのリストを使用する必要があります」です。これが私が思いついたものです...

#include <iostream>
#include <memory>
#include <list>

class Base
{
    public:
        virtual void foo()
        {
            std::cout << "Base::foo" << std::endl;
        }
};

class Derived : public Base
{
    public:
        void foo()
        {
            std::cout << "Derived::foo" << std::endl;
            Base::foo();
        }
};

int main()
{
    std::list<std::shared_ptr<Base>> bases;
    auto p = std::make_shared<Derived>();
    bases.push_back(p);

    for (std::list<std::shared_ptr<Base>>::iterator i = bases.begin(); i != bases.end(); i++)
    {
        (*i)->foo();
    }

    return 0;
}
于 2013-01-26T06:36:08.870 に答える