6

動的に割り当てられたオブジェクトをベクターに追加する必要があるときはいつでも、次の方法でそれを行ってきました。

class Foo { ... };

vector<Foo*> v;

v.push_back(new Foo);

// do stuff with Foo in v

// delete all Foo in v

それはうまくいっただけで、他の多くの人も同じことをしているようです。

今日、vector::push_back が例外をスローできることを学びました。つまり、上記のコードは例外セーフではありません。:-(だから私は解決策を思いついた:

class Foo { ... };

vector<Foo*> v;
auto_ptr<Foo> p(new Foo);

v.push_back(p.get());
p.release();

// do stuff with Foo in v

// delete all Foo in v

しかし、問題は、新しい方法が冗長で退屈であり、誰もそれを行っていないことです。(少なくとも私の周りでは…)

私は新しい道を歩むべきですか?
それとも、昔のやり方のままでいられますか?
または、それを行うより良い方法はありますか?

4

4 に答える 4

11

あなたの新しい方法はより例外的に安全ですが、それが他のどこにも見られないのには理由があります。

ポインターの Avectorはポインターを所有するだけで、ポイント先のオブジェクトの所有権を表していません。所有権を「望んでいない」オブジェクトに所有権を効果的に解放しています。

ほとんどの人は、所有権を正しく表すためvectorに のを使用するか、 のようなものを使用します。これらのいずれかは、エラーが発生しやすく、プログラムの他のポイントで例外が「危険」になる可能性がある、格納しているポインターを持つオブジェクトを明示的に指定する必要がないことを意味します。shared_ptrboost::ptr_vectordelete

編集:への挿入にはまだ十分注意する必要がありますptr_vector。残念ながら、push_back生のポインターを取得すると、挿入が成功するか、(事実上)何も起こらないことを意味する強力な保証が提供されるため、渡されたオブジェクトは引き継がれることも破棄されることもありません。スマート ポインターを値で.release()受け取るバージョンは、強力に保証されたバージョンを呼び出す前に呼び出すと定義されます。これは事実上、リークする可能性があることを意味します。

vectorをとshared_ptr一緒に使用するとmake_shared、正しく使用するのがはるかに簡単になります。

于 2010-11-15T14:43:19.860 に答える
11

あなたが気にするのは、この操作の例外安全性だけです:

v.reserve(v.size()+1);  // reserve can throw, but that doesn't matter
v.push_back(new Foo);   // new can throw, that doesn't matter either.

その内容が指すオブジェクトを解放する責任を持つベクトルの問題は別の問題です。それについては多くのアドバイスが得られると思います ;-)

編集:うーん、標準を引用するつもりでしたが、実際には必要な保証が見つかりません。私が探しているのはpush_back、(a)再割り当てする必要がある(容量のためにそうではないことがわかっている)か、(b)T throws のコンストラクター(そうでないことがわかっている)のいずれかでない限り、スローしないことです。 t (T はポインター型であるため)。合理的に聞こえますが、合理的です != 保証されています。

したがって、この質問に対する有益な回答がない限り:

std::vector::push_back は、再割り当てまたは構築の失敗以外の理由でスローすることを許可されていますか?

このコードは、実装が「想像力に富んだ」ことを何もしていないことに依存しています。それができない場合は、質問からの解決策をテンプレート化できます。

template <typename T, typename Container>
void push_back_new(Container &c) {
    auto_ptr<T> p(new T);
    c.push_back(p.get());
    p.release();
}

使い方はそれほど面倒ではありません:

struct Bar : Foo { };

vector<Foo*> v;
push_back_new<Foo>(v);
push_back_new<Bar>(v);

それが実際にファクトリ関数である場合は、newそれに応じてテンプレートを変更できます。ただし、さまざまな状況でさまざまなパラメーター リストを多数渡すのは困難です。

于 2010-11-15T14:46:46.650 に答える
3

これを行うための推奨される方法は、スマート ポインターのコンテナーを使用std::vector<std::shared_ptr<Foo> >することstd::vector<std::unique_ptr<Foo> >です。 shared_ptrstd::unique_ptr

もう 1 つのオプションは、Boost Pointer Containers ライブラリによって提供されるコンテナーなど、動的オブジェクトを所有するコンテナーを使用することです。

于 2010-11-15T14:43:46.057 に答える
0

あなたのプログラムはメモリ不足に対してどの程度回復力がありますか? あなたが本当にこれを気にするなら、あなたnewも同様に投げる準備をしなければなりません. push_backあなたがそれを処理するつもりがない場合、私はフープ を飛び越えることを心配しません.

一般的なケースでは、メモリが不足すると、制約のあるフットプリント (組み込みシステム) で永久に実行するように特別に設計されていない限り、プログラムはすでに克服できない問題を抱えている可能性があります。その場合、これらすべてのケースに注意する必要があります。

それが当てはまる場合は、コードのレビューと再テストのサイクルが長くなる可能性があります。ただし、ここでチームの慣行に従っても問題ないと思います。

他の人が指摘しているように、vector生のポインターを保存するために使用することには独自の問題があり、このサイトと他の回答には、より安全なパターンに導くための豊富な資料があります。

于 2010-11-15T14:42:51.780 に答える