2011-07-08 14 views
26

私はopenFrameworksを使用してアプリケーションを作成していますが、私の質問はoFだけではありません。むしろ一般的なC++ベクタに関する一般的な質問です。オブジェクトのC++ベクトルとオブジェクトへのポインタのベクトル

別のクラスの複数のインスタンスを含むクラスを作成すると同時に、それらのオブジェクトとやりとりするための直感的なインターフェイスも提供したいと考えていました。内部的にはクラスのベクトルを使用していましたが、vector.at()を使用してオブジェクトを操作しようとすると、プログラムはコンパイルされましたが正しく動作しませんでした(私の場合はビデオは表示されません)。どこか

// instantiate object dynamically, do something, then append to vector 
vector<ofVideoPlayer> videos; 
ofVideoPlayer *video = new ofVideoPlayer; 
video->loadMovie(filename); 
videos.push_back(*video); 

// access object in vector and do something; compiles but does not work properly 
// without going into specific openFrameworks details, the problem was that the video would 
// not draw to screen 
videos.at(0)->draw(); 

、それは私が代わりにそれらのオブジェクト自体のベクトルのそのクラスのオブジェクトへのポインタのベクトルを作ることが示唆されました。私はこれを実装しましたが、実際それは魅力的に機能しました。

vector<ofVideoPlayer*> videos; 
ofVideoPlayer * video = new ofVideoPlayer; 
video->loadMovie(filename); 
videos.push_back(video); 
// now dereference pointer to object and call draw 
videos.at(0)->draw(); 

私は、動的オブジェクト用のメモリを割り当てたすなわちofVideoPlayer = new ofVideoPlayer;

私の質問は簡単です:なぜポインタの仕事のベクトルを使用しなかった、とあなたはポインタのベクトル対オブジェクトのベクトルを作成したときにそれらのオブジェクトに?

+0

いくつかコードすることはできますか?ちょうど説明からこれに答えるのは少し難しい。 – MGZero

+0

投稿していないとコードがうまくいかなかった理由はわかりません!あなたの質問を紹介する例を追加してください。 – Cameron

+0

重要なコードは 'vector'の使用法ではなく、' ofVideoPlayer'クラスそのものです。 –

答えて

23

C++のベクトルについて知っておくべきことは、オブジェクトのクラスのコピー演算子を使用してベクトルに入力できることです。デストラクタが呼び出されたときに自動的に割り当てが解除されたこれらのオブジェクトにメモリが割り当てられていれば、そのオブジェクトはベクタにコピーされて破棄されました。

オブジェクトクラスで割り当てられたバッファを指すポインタがある場合、このオブジェクトのコピーは同じバッファを指します(デフォルトのコピー演算子を使用する場合)。デストラクタがバッファの割り当てを解除すると、コピーデストラクタが呼び出されると、元のバッファの割り当てが解除されるため、データはもう使用できなくなります。

この問題は、ポインタを使用すると発生しません。なぜなら、新しい/破壊を介して要素の寿命を制御し、ベクトル関数が要素に向かってポインタをコピーするだけなのでです。

+1

a)しかし、潜在的に危険な構築: 'ofVideoPlayer * video = new ofVideoPlayer;およびこのローカル変数をいくつかのベクトルに' push_back'します。 'video videos'がそれを使用するまで(例えば、例の場合と同じコンテキストで割り当てられている)、' video'がコンテキストを離れることがない場合にのみ動作することが保証されています。 b)値渡し(失敗したコード)がより安全です - なぜあなたのオブジェクトが 'ベクトル'にコピーされ**破壊されたのか理解していない**破壊された - なぜですか?一つは、間違った/コピーしていないコンストラクタの問題であると言わざるを得ない。 –

3

vectorオリジナルのオブジェクトのコピーを追加して内部のハウスキーピングを使用する - コピーを取ることが非常に高価または不可能な場合は、ポインタを使用することが望ましい。

vectorメンバをポインタにする場合は、smart pointerを使用してコードを単純化し、リークのリスクを最小限に抑えます。

あなたのクラスが適切な(つまり深い)コピーの作成/割り当てをしないのでしょうか?そうであれば、ポインターは動作しますが、ベクトルインスタンスとしてのオブジェクトインスタンスは動作しません。

+0

私はそれが適切な答えだと思いますが、アップアップしたものではありません。 –

6

newを使用してオブジェクトのメモリを割り当てる場合は、ヒープに割り当てます。この場合、ポインタを使用する必要があります。しかし、C++では、通常、スタック上のすべてのオブジェクトを作成し、そのオブジェクトのコピーをヒープ上のオブジェクトに渡す代わりに渡します。

これはなぜ優れていますか?これは、C++にガベージコレクションがないためです。具体的にはオブジェクトdeleteを除き、ヒープ上のオブジェクトのメモリは再利用されません。ただし、スタック上のオブジェクトは、スコープを離れると常に破棄されます。ヒープの代わりにスタックにオブジェクトを作成すると、メモリリークのリスクを最小限に抑えることができます。

ヒープの代わりにスタックを使用する場合は、適切なコピーコンストラクタとデストラクタを作成する必要があります。間違って書かれたコピーコンストラクタまたはデストラクタは、メモリリークまたは二重解放のいずれかにつながる可能性があります。

オブジェクトが大きすぎて効率的にコピーできない場合は、ポインタを使用することもできます。しかし、メモリリークを避けるために、リファレンスカウントのスマートポインタ(C++ 0x auto_ptrまたはBoostライブラリポインタのいずれか)を使用する必要があります。なぜポインタの仕事の ベクトルを使用しなかった、とするとき あなたはそれらの オブジェクトへのポインタのベクトル対オブジェクト のベクトルを作成します。

+0

'std :: auto_ptr'が標準です。 'std :: unique_ptr'の代わりにC++ 0xでは非推奨になります。 'std :: shared_ptr'、' std :: weak_ptr'なども紹介します。スタックの割り当ては必ずしも望ましいとは限りませんが、例外セーフヒープ管理のためにスマートポインタを使用することはできません。パフォーマンスのためにコールをコピーしないように、 'const&'オブジェクトを渡す方が良いでしょう。 – AJG85

12

私の質問は簡単なのですか?

std :: vectorは、newで割り当てられた生の配列のようなもので、現在のサイズよりも多くの要素をプッシュしようとすると再割り当てされます。

したがって、Aポインタが含まれている場合は、A *の配列を操作している場合と同じです。 サイズを変更する必要があるとき(elemetnがすでに現在の容量で満たされている間push_backする)、別のA *配列を作成し、前の配列からコピーします。

Aオブジェクトが含まれている場合、Aの配列を操作していたようです。そのため、自動配置が発生している場合、Aはデフォルト構成可能でなければなりません。この場合、Aオブジェクト全体が別の配列にもコピーされます。

違いをご確認ください。 内部配列のサイズ変更が必要な操作を行うと、std :: vectorのAオブジェクトはアドレスを変更できます。これは、std :: vectorにオブジェクトを格納する際の多くの問題が発生します。

このような問題を起こさずにstd :: vectorを使用する方法は、最初から十分な大きさの配列を割り当てることです。 ここのキーワードは「容量」です。 std :: vector capacityは、オブジェクトを配置するメモリバッファの実際のサイズです。したがって、容量を設定するには、2つの選択肢があります。最初からすべてのオブジェクトを構築するために、std :: vectorのサイズをオブジェクトの最大数で構築する - 各オブジェクトのコンストラクタを呼び出す2つの選択肢があります:

は、それがreserve()関数を使用します:(ベクトルの最大サイズを指定する)十分大きなバッファを割り当てます。 ITは容量を設定します。このベクトル内のオブジェクトをpush_backするか、reserve()呼び出しで指定したサイズの制限の下でresize()を実行すると、内部バッファは決して再配置されず、オブジェクトはメモリ内の位置を変更せず、常に有効です(キャパシティの変更が発生しないことを確認するためのアサーションが優れた方法です)。

+0

'A'は__default__コンストラクタブルである必要はありません.btw – RiaD

+3

これは、オブジェクトのプッシュバックによってベクトルのサイズが変更されたときに、ベクトルのオブジェクトへのメモリ参照が無効な領域を指している可能性があることについての特に良い答えです。 – nurabha

+1

。 – TREMOR

2

通常、私は直接クラスをstd::vectorに保存しません。理由は簡単です。クラスが派生したかどうかはわかりません。

など。ソースにおいて

class base 
{ 
public: 
    virtual base * clone() { new base(*this); }; 
    virtual ~base(){}; 
}; 
class derived : public base 
{ 
public: 
    virtual base * clone() { new derived(*this); }; 
}; 
void some_code(void); 
void work_on_some_class(base &_arg); 

void some_code(void) 
{ 
    ... 
    derived instance; 
    work_on_some_class(derived instance); 
    ... 
} 

void work_on_some_class(base &_arg) 
{ 
    vector<base> store; 
    ... 
    store.push_back(*_arg.clone()); 
    // Issue! 
    // get derived * from clone -> the size of the object would greater than size of base 
} 

だからshared_ptrを使用することを好む:

void work_on_some_class(base &_arg) 
{ 
    vector<shared_ptr<base> > store; 
    ... 
    store.push_back(_arg.clone()); 
    // no issue :) 
} 
2

ベクターを使用することの主なアイデアは、内のオブジェクトを格納することである:ヘッダ内

ポインタやスマートポインタを使用しているときにスペースが続く場合は、

関連する問題