std :: vectorはpush_backでオブジェクトをコピーしていますか?


169

valgrindで多くの調査を行った後、std :: vectorがpush_backするオブジェクトのコピーを作成するという結論に達しました。

それは本当ですか?コピーなしでは、ベクターはオブジェクトの参照またはポインターを保持できません?!

ありがとう


20
これがC ++の基本原理です。オブジェクトは値です。割り当てはコピーを作成します。あなたと種類を変更しない限り、同じオブジェクトを参照する二つの変数は可能ではない*か、&ポインタまたは参照を行うこと。
Daniel Earwicker、

8
@DanielEarwicker push_backは実際には参照を受け取ります。それがコピーを作成するかどうかは、署名だけでは明らかではありません。
ブライアンゴードン

3
@BrianGordon-そうではありません!したがって、指導原則の必要性。たとえそうだとしても、我々はの署名から何かを推測することができますpush_back:それが取りますconst&。値を捨てる(役に立たない)か、取得方法があります。したがって、のシグネチャを確認すると、backplainが返される&ので、元の値がコピーされたか、constが黙ってキャストされました(非常に悪い:未定義の動作の可能性があります)。したがって、の設計者vectorが合理的である(vector<bool>耐えられない)と仮定して、コピーを作成すると結論付けます。
Daniel Earwicker 2014

回答:


183

はい、std::vector<T>::push_back()引数のコピーを作成してベクターに保存します。オブジェクトへのポインターをベクターに保存する場合は、std::vector<whatever*>ではなくを作成しますstd::vector<whatever>

ただし、ベクトルがオブジェクトへの参照を保持している間、ポインターによって参照されるオブジェクトが有効であることを確認する必要があります(RAIIイディオムを利用したスマートポインターが問題を解決します)。


また、生のポインターを使用する場合は、後からクリーンアップする必要があります。これを行う正当な理由はありません(とにかく私が考えることはできません)、常にスマートポインターを使用する必要があります。
Ed S.

1
それはあなたがより多くの情報については、STLコンテナにはstd :: auto_ptrを使用しないでください、言った:なぜ-あるに使える-それ-間違っ-stdauto-PTR-と標準コンテナ
OriginalCliche

24
C ++ 11以降push_back、引数が右辺値参照の場合、コピーではなく移動を実行します。(オブジェクトはstd::move()。を使用して右辺値参照に変換できます。)
emlai

2
@tuple_catコメントには、「引数が右辺値の場合」と記載する必要があります。(引数が右辺値参照として宣言されたエンティティの名前である場合、引数は実際には左辺値であり、そこから移動されません)-最初にその間違いを犯した "カールニコル"の回答に対する私の編集を確認してください
MM

以下に回答がありますが、明確にするためです。C++ 11はemplace_back、コピーや移動を回避するためにも使用するためです(コンテナーによって提供されるオブジェクトを適切な場所に構築します)。
ワーマー、

34

はい、std::vectorコピーを保存します。vectorオブジェクトの予想寿命をどのように知る必要がありますか?

オブジェクトの所有権を転送または共有する場合は、ポインターを使用します(おそらくBoostまたはTR1にあるようなshared_ptr)スマートポインターを使用して、リソース管理を容易にします。


3
shared_ptrの使い方を学ぶ-彼らはまさにあなたが望むことをします。私の好きなイディオムはtypedef boost :: shared_ptr <Foo> FooPtrです。次に、FooPtrsのコンテナを作成します
pm100

3
@ pm100-知っていますかboost::ptr_vector
マヌエル

2
私はまた、使いたいclass Foo { typedef boost::shared_ptr<Foo> ptr; };だけのライトにFoo::ptr
Rupert Jones

2
@ pm100- shared_ptr正確に言うと忘れないでください。stackoverflow.com/questions/327573および stackoverflow.com/questions/701456を
Daniel Earwicker

2
所有権を共有している場合は、shared_ptrが適切ですが、一般的に過剰に使用されます。unique_ptrまたはboost scoped_ptrは、所有権が明確である場合に、より意味があります。
Nemanja Trifunovic 2010

28

C ++ 11以降、すべての標準コンテナ(からstd::vectorstd::mapなど)のサポートムーブセマンティクス、あなたが今標準コンテナへの右辺値を渡すとコピーを避けることができることを意味:

// Example object class.
class object
{
private:
    int             m_val1;
    std::string     m_val2;

public:
    // Constructor for object class.
    object(int val1, std::string &&val2) :
        m_val1(val1),
        m_val2(std::move(val2))
    {

    }
};

std::vector<object> myList;

// #1 Copy into the vector.
object foo1(1, "foo");
myList.push_back(foo1);

// #2 Move into the vector (no copy).
object foo2(1024, "bar");
myList.push_back(std::move(foo2));

// #3 Move temporary into vector (no copy).
myList.push_back(object(453, "baz"));

// #4 Create instance of object directly inside the vector (no copy, no move).
myList.emplace_back(453, "qux");

または、さまざまなスマートポインターを使用して、ほとんど同じ効果を得ることができます。

std::unique_ptr

std::vector<std::unique_ptr<object>> myPtrList;

// #5a unique_ptr can only ever be moved.
auto pFoo = std::make_unique<object>(1, "foo");
myPtrList.push_back(std::move(pFoo));

// #5b unique_ptr can only ever be moved.
myPtrList.push_back(std::make_unique<object>(1, "foo"));

std::shared_ptr

std::vector<std::shared_ptr<object>> objectPtrList2;

// #6 shared_ptr can be used to retain a copy of the pointer and update both the vector
// value and the local copy simultaneously.
auto pFooShared = std::make_shared<object>(1, "foo");
objectPtrList2.push_back(pFooShared);
// Pointer to object stored in the vector, but pFooShared is still valid.

2
std::make_unique(厄介なことに)C ++ 14以降でのみ利用可能であることに注意してください。これらの例をコンパイルする場合は、コンパイラに標準の準拠を設定するように指示してください。
Laryx Decidua 2017

5aではauto pFoo =、繰り返しを避けるために使用できます。すべてのstd::stringキャストを削除できます(文字列リテラルからへの暗黙の変換がありますstd::string
MM

2
@ user465139 make_uniqueはC ++ 11で簡単に実装できるため、C ++ 11コンパイラーで立ち往生している人にとってはほんの少しの煩わしさです
MM

1
@MM:確かに。ここでは教科書の実装は次のとおりです。template<typename T, typename... Args> unique_ptr<T> make_unique(Args&&... args) { return unique_ptr<T>{new T{args...}}; }
Laryx脱落膜

1
@アナキン-はい、彼らはすべきですが、あなたがコピーした場合に限ります。std::move()とともに使用する場合std::shared_ptr、所有権がベクターに渡されたため、元の共有ポインターのポインターが変更されている可能性があります。ここを参照してください:coliru.stacked-crooked.com/a/99d4f04f05e5c7f3
Karl Nicoll

15

std :: vectorは常にベクターに保存されているもののコピーを作成します。

ポインタのベクトルを保持している場合は、ポインタのコピーが作成されますが、ポインタが指しているインスタンスは作成されません。大きなオブジェクトを扱う場合は、常にポインターのベクトルを使用できます(おそらく使用する必要があります)。多くの場合、適切なタイプのスマートポインターのベクトルを使用すると、オブジェクトの有効期間とメモリ管理の処理が難しいため、安全上の理由から優れています。


3
タイプには依存しません。常にコピーを作成します。そのポインタがポインタのコピーを作成する場合
pm100

あなたは両方正しいです。技術的には、はい、常にコピーを作成します。実際には、オブジェクトへのポインタを渡すと、オブジェクトではなくポインタがコピーされます。安全に、適切なスマートポインタを使用する必要があります。
Steven Sudit、2010

1
はい、常にコピーしています-ただし、OPが参照する「オブジェクト」はクラスまたは構造体である可能性が高いため、「オブジェクト」をコピーするかどうかは定義に依存するかどうかを参照していました。言葉遣いが悪いけど。
リードコプシー2010

3

std :: vectorは、押し戻しているもののコピーを作成するだけでなく、コレクションの定義により、コピーが作成され、ベクター内で正しいコピーセマンティクスなしにオブジェクトを使用することはできないと規定されています。したがって、たとえば、ベクターでauto_ptrを使用しないでください。


2

C ++ 11に関連するのは、emplaceオブジェクトをコンテナーに移動することによってオブジェクトの所有権を転送できるメンバー関数のファミリーです。

使用法のイディオムは次のようになります

std::vector<Object> objs;

Object l_value_obj { /* initialize */ };
// use object here...

objs.emplace_back(std::move(l_value_obj));

左辺値オブジェクトの移動は重要です。移動しないと、参照またはconst参照として転送され、移動コンストラクターが呼び出されません。


0

コピーしたくない場合。次に、最良の方法は、ポインターベクトル(または同じ目標に役立つ別の構造)を使用することです。コピーが必要な場合。直接push_back()を使用してください。他に選択肢はありません。


1
ポインタベクトルに関する注意:vector <shared_ptr <obj>>はvector <obj *>よりもはるかに安全で、shared_ptrは昨年の標準の一部です。
rich.e 2012年

-1

なぜこれを見つけるのに多くのヴァルグリンド調査が必要だったのですか?いくつかの簡単なコードで自分に証明してください。

std::vector<std::string> vec;

{
      std::string obj("hello world");
      vec.push_pack(obj);
}

std::cout << vec[0] << std::endl;  

「hello world」が印刷されている場合、オブジェクトはコピーされているはずです


4
これは証明とはなりません。オブジェクトがコピーされなかった場合、最後のステートメントは未定義の動作となり、hello を出力する可能性があります。
マット

4
正しいテストは、挿入後に2つのうちの1つを変更することです。それらが同じオブジェクトである場合(ベクターが参照を格納している場合)、両方が変更されます。
Francesco Dondi 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.