3

これは、手書きのループなどの代わりにSTLアルゴリズムを使用して、古い宿題を書き直そうとする試みです。

私はデータベースと呼ばれるクラスを持っています。このクラスではVector<Media *>、Media *は(とりわけ)CDまたはBookになります。データベースは動的メモリを処理する唯一のクラスであり、プログラムが起動すると、以下のようにフォーマットされたファイルを読み取り(多少簡略化)、エントリを読み取るときにスペースを割り当て、上記のベクトル(v_)に追加します。

CD
アーティスト
アルバム
ID番号
本
著者
タイトル
ID番号
本
..。
..。

手書きのループを使用すると、これらのオブジェクトの削除は期待どおりに機能します。編集:申し訳ありませんが、話が早すぎました。実際には「手書きの」ループではありません。プロジェクトを編集して、手書きのループを削除しています。これは実際にはfind_ifアルゴリズムと手動削除を使用しますが、問題は有効になるまでです。/編集。

typedef vector<Media *>::iterator v_iter;

..。
void Database :: removeById(int id){
    v_iter it = find_if(v_.begin()、v_.end()、Comparer(id));
    if(it!= v_.end()){
        消して;
        v_.erase(it);
    }
}

これはかなり自明のはずです。パラメータに一致するIDが見つかった場合、ファンクターはtrueを返し、オブジェクトは破棄されます。これは機能し、valgrindはメモリリークを報告しませんが、STLを使用したいので、明らかな解決策は、erase-removeイディオムを使用することです。

void Database :: removeById(int id){
    v_.erase(remove_if(v_.begin()、v_.end()、Comparer(id))、v_.end());
};

ただし、これは「機能」しますが、valgrindによるとメモリリークが発生します。最初のバージョンはリークなしで正常に動作しますが、このバージョンでは、削除するすべてのメディアオブジェクトに対して「解放されていない」3つの割り当てが常に表示されます。

4

7 に答える 7

4

ルールは次のとおりです。ポインタを含み、ポインタの所有者であるベクトルにremove()を適用すると、メモリがリークします。

ScottMeyersによる「EffectiveSTL」には素晴らしい解決策があります。そして、それはスマートポインタを含みません!スマートポインタを使用して消去-削除を機能させるのはやり過ぎです。

これが(本のアイテム33から)です。タスクは、isCertified()がfalseを返すベクトルウィジェットから選択的に削除することです。

class Widget
{
  public:
    isCertified() const;
  ...
};

void delAndNullifyUncertified(Widget*& pWidget)
{
  if (!pWidget->isCertified())
  {
    delete pWidget;
    pWidget = 0;
  }
}

vector<Widget *> v;
v.push_back(new Widget);
...
// set to NULL all uncertified widgets
for_each(v.begin(), v.end(), delAndNullifyUncertified);
// eliminate all NULL pointers from v
v.erase(remove(v.begin(), v.end(), static_cast<Widget *>(0)),
        v.end();

これがお役に立てば幸いです。

編集(2012年8月28日)Slavik81からの有効な観察を反映するため

于 2012-08-27T23:42:55.220 に答える
3

最初のバージョンでは、慎重にを呼び出していますdelete *it。更新されたバージョンではv_.erase、ポインタの割り当てを解除しているわけではありませんが、ポインタが参照しているオブジェクトは割り当て解除されていません。

于 2010-07-18T15:05:51.330 に答える
3

これが、常にスマートポインタを使用する必要がある理由です。問題が発生する理由は、ダムポインタを使用し、それをベクトルから削除したが、それが指しているメモリを解放するトリガーにはならなかったためです。代わりに、常にポイント先のメモリを解放するスマートポインタを使用する必要があります。ここで、ベクトルからの削除は、ポイント先のメモリを解放することと同じです。

于 2010-07-18T15:08:57.997 に答える
2

あなたはすでに特定の答えを持っています。ただし、根本的な問題は、リソースを手動で管理するためにネイキッドポインターを使用していることです。それは難しいです、そして時々それは痛いです。
タイプをに変更するstd::vector<std::shared_ptr<Media> >と、作業がはるかに簡単になります。

(コンパイラがまだサポートしていない場合は、サポートstd::shared_ptrしている可能性が非常に高くなりますstd::tr1::shared_ptr。それ以外の場合は、boostを boost::shared_ptr使用してください。)

于 2010-07-18T15:08:52.513 に答える
1

deleteの2番目のバージョンではへの呼び出しはありませんremoveById。ポインターのベクトルから要素を消去しても、ポインターは呼び出されませんdelete

提示された消去-削除バージョンは、元のバージョンを使用するのとほぼ同じですremoveByIdが、わずかな変更があります。

void Database::removeById(int id) {
    v_iter it = find_if(v_.begin(), v_.end(), Comparer(id));
    if (it != v_.end()) {
        //delete *it;
        v_.erase(it);
    }
}

うまくいけば、これにより、何が起こっているのか、そしてなぜリークが発生しているのかが明確になります。

于 2010-07-18T15:08:54.073 に答える
1

明らかに、あなたvectorはそれが含むオブジェクトを所有しています。

お気づきのように、STLコンテナーは、継承に簡単に適応できないという意味でOOに対応していません。そのためには、動的に割り当てられたメモリとそのすべての問題を使用する必要があります。

最初の簡単な解決策は、プレーンポインター(これ以上はしないでください)をスマートポインターに置き換えることです。人々は通常お勧めshared_ptrしますが、C ++ 0xにアクセスできる場合は、それらをお勧めしますunique_ptr

あなたが考慮すべき別の解決策もあります。STLコンテナを模倣しているが、継承階層で動作するように設計されているコンテナ。それらは(明らかに)内部でポインターを使用しますが、メモリーを自分で管理するという面倒な作業や、スマートポインターに関連するオーバーヘッド(特にshared_ptr)から解放されます。

Boost Pointer Containerライブラリに注目してください!

それは明らかにあなたが解決しようとしている問題のために正確に作成されているので、ここで最良の解決策です。

erase/removeさらに、そのコンテナは(ほとんどの場合)STLに準拠しているため、イディオムやfind_ifアルゴリズムなどを使用できます...

于 2010-07-19T06:59:16.503 に答える
0

これは、2番目のバージョンが正しくないことを意味します。それを使用したい場合は、shared_ptrを検討してください。

typedef vector <shared_ptr <Media>> MediaVector;
..。
于 2010-07-18T15:08:01.313 に答える