2016-08-05 109 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被首先插入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。 我不明白,這兩種方法之間的差異。爲什麼在插入元素後列表必須添加?

+3

在第一種情況下,您需要更改'list1.push_back(1); list1.push_back(2);'to'vec.back()。push_back(1); vec.back()。push_back(2);'否則你只需要添加項目到'list1'而不是'vec'。 – DimChtz

+0

順便說一句,你的第三個解決方案(指針)基本上不起作用的原因是第一種情況不起作用,因爲你'push_back()'list3'的取消引用(只是list3的值)。 – DimChtz

+0

在@DimChtz上展開評論:當您將某些東西放入「矢量」時,「矢量」存儲副本。操作原稿將不會影響副本。 – user4581301

回答

1

vector.push_back(var)製作var的副本並將其插入到載體中。如果您在空列表上使用push_back(),則會將空列表複製到矢量中。在此之後更改列表中的值不會影響插入向量中的副本。這是因爲您正在將實際對象傳遞給push_back(),而不是指向對象的指針。

在第三個示例中,您向正確的方向邁出了一步,但在您傳入該列表之前您會取消引用該列表,因此push_back()會將該地址的內容複製一份。

該問題的一個簡單解決方案是在將列表插入到向量中之前始終設置值。

如果您希望能夠在列表插入後更改值,請使用vect.at(i).push_back(val)向i中的列表添加值。 你也可以將矢量包含指向其他載體,而不是載體本身:

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

不用擔心,記得在完成時調用'delete',因爲它們被分配了'new' – naffarn

+1

@iamseiko他們不是。如果你沒有準備好,'vector's中的指針正在邀請一個地獄的世界。 – user4581301

0

當你把東西放到一個std::vectorvector存儲副本。操作原稿將不會影響副本。如果您將指針放入指針的vector中,則vector仍會存儲該指針的副本。原始文件和vector中的副本都指向相同的內存,因此您可以操縱引用的數據並查看引用數據中的更改。

所以......

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

把空list1進入副本vec。然後將1和2的副本放入原始的list1list1vec的副本不受影響。

寫這爲

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遊逛塞滿了範圍。

而且

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

會,如果你的編譯器支持C++ 11或更高進一步簡化。

在另一方面...

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

提出的3和4份爲list2,然後提出的list2副本,完成和3個副本的複印件4.

類似以上,

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

可以減少工作量。

不幸的是,因爲在list3vec不持有指針的指針,所以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完成之前不會被銷燬。一種解決方案是動態分配存儲。
  2. (這適用於一般動態分配的指針)如果你動態分配,遲早有人必須清理這些混亂和delete這些指針。查看storing smart pointers而不是原始指針,並且根本不存儲指針。
  3. 熟悉the Rule of Threevector照看自己,但如果你有兩個vector的副本,並且你只從其中一箇中刪除並刪除一個指針,那麼你將要進行一些調試。
2

它幾乎看起來像你期待的python般的行爲?無論如何,在C++中,引用,指針和值之間的區別非常重要。

您的fillVector功能有正確的想法,因爲它需要參考2D矢量std::vector<std::vector<int> > &vec - 請注意&。然而,當你創建list1,並使用push_back()馬上

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

你是推空載體。 push_back()將創建這個矢量的拷貝,它將包含在vect(在main),並且是一個完全獨立的向量從list1

此時,如果要訪問已推送的矢量,可以使用back(),它返回對矢量vec中最後一個元素的引用,即最後一個推送的元素。

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

list2你推回之前修改,因此複製時作出,它是由已修改的載體。您嘗試list3確實沒有太大的改變,當您push_back()和副本完全相同時,您將指針取消引用。您可以讓vectstd::vector<std::vector<int>*>,但我強烈建議您不要這樣做,因爲您必須執行手動內存管理 - 使用new

注:雖然它讓你在某些時候學習很重要,你應該儘量避免使用指針只要有可能,特別是原始指針(看smart pointers代替)。 std::vector,以及我知道的所有其他std容器,都做他們自己的內存管理 - 他們一定會比你更有效地做到這一點,而且無BUG。


我建議你只需在最後的矢量工作推,因爲這樣的:

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; 
} 

,你可以看到它幾乎相同的代碼重複兩次,所以你可以很容易地循環得到這個或其他結果。