1

今日、コーディング中に奇妙な状況に遭遇しました。誰かがなぜこれが起こっているのかを明らかにしてくれることを願っています。

私はいくつかの基本クラスへのポインタのリストを持っています:

std::list<BaseClass*> m_list;

次に、このリストからBaseClassポインターの1つを取得します

BaseClass* pBase = m_list.front();

次に、この基本クラスをその子クラスの1つに変換します。(ここで奇妙さが出てくると思います)

pBase = new ChildClass(*pBase);

ChildClassは、BaseClassesコピーコンストラクターを使用して、すべてのBaseClassesフィールドをコピーします。

このChildClassを使用して、BaseClassesメソッドの1つを呼び出して、BaseClassにフィールドを設定します。

pBase->SetSomeIntMember(10);

このint値をチェックすると、期待どおりに10になりますが、リストから同じChildClassを再度取得して、intメンバーをチェックすると変更されないため、ローカルでのみ変更されているように見えます。

うまくいけば、これは従うのにそれほどトリッキーではありませんでした。これは何が起こるのですか?ポリモーフィズムが関与していない状況では、クラスインスタンスへのポインタがあるため、明らかにローカルな変更だけではありません。新しいChildClassを作成するときにポインターを踏みつけていると思いますが、仮想メソッドが引き続き機能するため、リストのBaseClassがChildClassになることは間違いありません。

4

4 に答える 4

3
 pBase = new ChildClass(pBase);

これは、「リストのBaseClassをChildClassにする」ことではありません。ChildClassの新しいインスタンスを作成します。のコンストラクターpBaseで行われた変更のみが、前に指し示したものに影響を与える可能性があります。(1つのクラスを「子クラスのインスタンスにする」ことはできません。)ChildClasspbase

そのコード行はまったく変更m_listされません。元のオブジェクトm_listへのポインタがまだ含まれています。BaseClass

于 2012-05-13T06:08:46.720 に答える
2

一見すると、新しく割り当てられたポインタを値でpBaseに割り当てているだけです。list要素は、実際には値によってpBaseにコピーされたポインタアドレスです。list要素は実際には変更されませんでした

代わりにこれを試してください

BaseClass** pBase = &(m_list.front());
BaseClass* pOld = *pBase;
*pBase = new ChildClass(**pBase); // you have a leak here of *pBase BTW 
deleteOrCleanup(pOld); // cleanup or delete the old pBase pointer 

//do what you were doing
于 2012-05-13T06:08:54.443 に答える
2

ポインターへの参照ではなく、ポインターの値をコピーします。

あれは、

BaseClass* pBase = m_list.front();
pBase = new ChildClass(*pBase);

と同じではありません

Baseclass*& pBase_r = m_list.front();
pBase_r = new ChildClass(*pBase_r);

元の値を更新する場合は、参照またはポインターを使用する必要があることに注意してください。

ノート

pBase2番目の例には、の元の値がの前に破棄されるため、メモリリークが含まれていますdelete。このような驚きを避けるために、std::shared_ptr<T>(C ++ 11)などのスマートポインタを使用するかboost::shared_ptr<T>、の代わりに使用してT*ください。

そのセマンティクスはSTLコンテナと互換性がないため、使用しないでください。std::auto_ptr<T>

したがって、リストクラスはである必要がありますstd::list<std::shared_ptr<BaseClass>>。ここでのもう1つの利点は、内部参照カウントを台無しにすることなく、参照の代わりにスマートポインターのインスタンスを使用できることです。

于 2012-05-13T06:09:36.830 に答える
2

他の人が指摘しているように、あなたの問題は、ポインタのローカルコピーを変更しているだけであり、実際に指しているものではないということです。

コンテナ要素を置き換えようとするときに、生のポインタをコンテナに貼り付けて手動で削除(またはメモリリーク)する代わりに、スマートポインタを使用します。

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

struct base
{
  base( int a )
  : x(a)
  {}

  int x;
};

struct derived : base
{
  derived( int a )
  : base(a)
  {}
};

int main()
{
  std::list<std::unique_ptr<base>> mylist;
  mylist.push_back( std::unique_ptr<base>( new derived(10) ) );

  auto pbase = mylist.front().get();    // get raw pointer to first element
  std::cout << pbase->x << std::endl;

  pbase = new derived( 10 * pbase->x ); // create a new derived object
  mylist.front().reset( pbase );        // replace the first element, previous 
                                        // element is deleted automatically

  pbase = mylist.front().get();
  std::cout << pbase->x << std::endl;

  // all allocated objects will be automatically deleted
  // when mylist goes out of scope
}

出力:

10
100
于 2012-05-13T06:28:13.637 に答える