2016-08-05 20 views
0

私はC++ベクトルで練習していましたが、要素を2Dベクトルに挿入するときに問題が見つかりました。次の例で:C++で2Dベクトルに要素を挿入する順序

#include <iostream> 
#include <vector> 

void fillVector(std::vector<std::vector<int> > &vec) { 
    std::vector<int> list1; 
    vec.push_back(list1); 
    list1.push_back(1); 
    list1.push_back(2); 

    std::vector<int> list2; 
    list2.push_back(3); 
    list2.push_back(4); 
    vec.push_back(list2); 

    return; 
} 

int main() { 
    std::vector<std::vector<int> > vect; 
    fillVector(vect); 

    std::cout << "vect size: " << vect.size() << std::endl; 

    for(int i = 0; i < vect.size(); i++) { 
     std::cout << "vect in size: " << vect.at(i).size() << std::endl; 
    } 
} 

第一インナーリストのサイズが0であり、及び第2の内部リストのサイズはlist1list2との間の唯一の違いはlist1が最初に挿入されることである2です。 vec要素が挿入される前の2Dベクトル。最初に要素がlist2に挿入されてから、2Dベクトルに挿入されます。関数から戻ると、list1に挿入された要素は印刷されず、そのサイズは変わりません。

私はまた、ポインタを持つ第一の方法ではなく、

std::vector<int> *list3 = new std::vector<int>(); 
vec.push_back(*list3); 
list3->push_back(5); 
list3->push_back(6); 

しかし、呼び出し元の関数から読み取るLIST3のサイズは依然として0 である私は2つのアプローチの違いを理解していない試みました。要素が挿入された後にリストを追加する必要があるのはなぜですか?

+3

最初に、 'list1.push_back(1);を変更する必要があります。 list1.push_back(2); 'to' vec.back()。push_back(1); vec.back()。push_back(2); 'そうでなければ' vec'ではなく 'list1'に項目を追加するだけです。 – DimChtz

+0

Btw、 'list3'の逆参照(list3の値だけ)を' push_back() 'するので、あなたの3番目の解決法(ポインタ)は本質的に機能しません。 – DimChtz

+0

@DimChtzコメントで展開: 'vector'に何かを入れると、' vector'はコピーを保存します。オリジナルを操作することはコピーに何の影響も与えません。 – user4581301

答えて

1

vector.push_back(var)varのコピーをベクターに挿入します。空のリストにpush_back()を使用すると、空のリストがベクターにコピーされます。この後にリスト内の値を変更しても、ベクトルに挿入されたコピーには影響しません。これは、オブジェクトへのポインタではなく、実際のオブジェクトをpush_back()に渡すためです。

3番目の例では、正しい方向にステップを実行しますが、渡す前にリストの参照を外すので、push_back()はそのアドレスにあるもののコピーを作成します。

問題を簡単に解決できるのは、リストをベクターに挿入する前に常に値を設定することです。

リストを挿入した後で値を変更したい場合は、vect.at(i).push_back(val)を使用してiのリストに値を追加します。あなたがstd::vectorに何かを置くときvector店コピー、

void fillVector(std::vector<std::vector<int> *> &vec) { 
    std::vector<int> *list1 = new std::vector<int>(); //Remember to allocate memory since we're using pointers now 
    list1->push_back(1); 
    list1->push_back(2); 
    vec.push_back(list1); // Copy the pointer that is list1 into vec 

    std::vector<int> *list2 = new std::vector<int>(); 
    vec.push_back(list2); // Copy the pointer that is list2 into vec 
    list2->push_back(3); 
    list2->push_back(4); 
    return; 
} 

int main() { 
    std::vector<std::vector<int> *> vect; // Vector of pointers to vectors 
    fillVector(vect); 

    std::cout << "vect size: " << vect.size() << std::endl; 

    for(int i = 0; i < vect.size(); i++) { 
     std::cout << "vect in size: " << vect.at(i)->size() << std::endl; 
    } 
} 
std::vector<std::vector<int> *> vec = new; // Vector of pointers 
+0

とてもうれしく説明していただきありがとうございます。私はポインタを使用するソリューションが好きですが、この例ではより安全なようです。 – iamseiko

+0

あなたは 'new'で割り当てられているので、終了時に' delete'を呼び出すことを心配しないでください。 – naffarn

+1

@iamseikoそうではありません。あなたが準備されていなければ、 'ベクトル'のポインタは地獄の世界を招待しています。 – user4581301

0

: また、ベクターを作ることができる他のベクターではなく、ベクトル自分自身へのポインタが含まれています。オリジナルを操作することはコピーに何の影響も与えません。ポインターをvectorのポインターに入れると、vectorにはまだポインターのコピーが保存されます。 vectorの元とコピーの両方が同じメモリを指しているので、参照データを操作して参照データの変更を参照することができます。

そう...

std::vector<int> list1; 
vec.push_back(list1); 
list1.push_back(1); 
list1.push_back(2); 

vecに空list1のコピーを置きます。その後、1と2のコピーが元のlist1に入れられます。 のコピーはvecには影響しません。

std::vector<int> list1; 
vec.push_back(list1); 
vec.back().push_back(1); 
vec.back().push_back(2); 

としてこれを書く

は、これを修正します。少しクリーナーバージョン

vec.push_back(std::vector<int>()); 
vec.back().push_back(1); 
vec.back().push_back(2); 

それはスコープを散らかすの周りにぶら下がっ廃棄物list1を持っていないようにするからです。あなたのコンパイラがC++ 11以上をサポートしている場合

そして

vec.push_back(std::vector<int>{1,2}); 

はさらに簡略化されます。

一方...

std::vector<int> list2; 
list2.push_back(3); 
list2.push_back(4); 
vec.push_back(list2); 

list2に3と4のコピーを置き、その後list2のコピーを置き、同様に3と4

のコピーのコピーとの完全な

std::vector<int> list2{3,4}; 
vec.push_back(list2); 

は、作業負荷を軽減できます。 list3がポインタを保持していないvecポインタである一方で、そうlist3が逆参照され、参照vectorがコピーされるため

は残念ながらlist3を使用して実験が失敗します。データlist3の参照へのポインタは格納されておらず、vecには上記と同じ理由で空のベクトルが含まれています。

std::vector<int> *list3 = new std::vector<int>(); 
vec.push_back(*list3); 
list3->push_back(5); 
list3->push_back(6); 

vector

  1. vectorで保存ポインタには、いくつかの注意事項はポインタのコピーを格納します。指すデータは、vectorを実行する前に破壊されないようにスコープを設定する必要があります。 1つの解決策は、ストレージを動的に割り当てることです。
  2. (これは一般的に動的に割り当てられたポインタに当てはまります)動的に割り振る場合、遅かれ早かれ誰かが混乱を取り除き、deleteそれらのポインタをクリーンアップする必要があります。未加工のポインタではなく、ポインタをまったく格納しないでください。storing smart pointersを参照してください。
  3. the Rule of Threeに慣れてください。 vectorはそれ自身を見ていますが、vectorのコピーが2つあり、そのうちの1つからポインタを削除して削除すると、デバッグするしかありません。
2

ほとんどあなたはPythonのような動作を期待しているようですか?いずれにせよ、C++では、参照、ポインタ、および値の区別は非常に重要です。

fillVectorの機能は、2Dベクトルstd::vector<std::vector<int> > &vecへの参照を取ります。&に気づいてください。あなたがlist1を作成し、すぐに

std::vector<int> list1; 
vec.push_back(list1); 

push_back()を使用する場合ただし、空のベクターを推進しています。 push_back()vectmain)に含まれるこのベクターのコピーを作成し、list1とは完全に別のベクターです。を使用すると、ベクトルvecの最後の要素(最後にプッシュされた要素)への参照が返されます。back()を使用すると、ベクトルvecの最後の要素への参照が返されます。

vec.back().push_back(1); 
vec.back().push_back(2); 

list2あなたが戻ってプッシュする前に修正し、そのコピーが作成されるとき、それはすでに修正ベクトルで構成されています。 list3であなたの試みは実際に多くを変えません。push_back()とコピーがすべて同じになったときにポインタを間接参照してください。 vectstd::vector<std::vector<int>*>とすることもできますが、手動メモリ管理を行う必要があるので、私は強くアドバイスしたいと思います。newを使用してください。

注:あなたには、いくつかの時点で学習することが重要ですが、あなたは本当にポインタ可能な限り、特別にRAWポインタ(代わりにsmart pointersを見て)利用するのは避けるべきです。 std::vectorと、私が知っている他のすべてのstdコンテナは、独自のメモリ管理を行います - あなたよりも効率的にそれを行うと確信しています。


プッシュ私のような、あなたは、単に最後のベクトル上で動作していることを示唆している:あなたは簡単にループを取得することができますので、あなたはそれだ見ることができるよう

void fillVector(std::vector<std::vector<int> > &vec) { 
    vec.push_back(std::vector<int>()); 
    vec.back().push_back(1); 
    vec.back().push_back(2); 

    vec.push_back(std::vector<int>()); 
    vec.back().push_back(3); 
    vec.back().push_back(4); 
    return; 
} 

はほとんど同じコードを2回繰り返しこれまたは他の結果。

関連する問題