在c ++中将元素插入2D Vector的顺序

时间:2016-08-05 00:21:56

标签: c++ pointers vector

我正在使用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 个答案:

答案 0 :(得分:2)

你好像期待像python一样的行为?无论如何,在C ++中,引用,指针和值之间的区别非常重要。

您的fillVector函数有正确的想法,因为它需要引用2D向量std::vector<std::vector<int> > &vec - 请注意&。但是,当您创建list1时,立即使用push_back()

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

你正在推空矢量。 push_back()将创建此向量的副本,该副本将包含在vectmain)中,并且是list1中完全独立的向量。

此时,如果要访问已推送的向量,可以使用back(),它返回对向量vec中最后一个元素的引用,即最后一个元素被推送

vec.back().push_back(1);
vec.back().push_back(2);
在推回之前修改

list2,因此在复制时,它由已修改的向量组成。你list3的尝试并没有真正改变,你在push_back()和副本完全相同时取消引用指针。您可以将vect设为std::vector<std::vector<int>*>,但我强烈反对,因为您必须使用new进行手动内存管理。

注意:虽然在某些时候学习很重要,但是你应该尽可能避免使用指针,特别是RAW指针(请看smart pointers)。 std::vector,以及我所知道的所有其他std容器,都做自己的内存管理 - 他们肯定比你更有效率,并且BUG FREE。

我建议你简单地处理推送的最后一个向量,如下:

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

正如您所看到的那样,重复两次相同的代码,因此您可以轻松循环以获得此结果或其他结果。

答案 1 :(得分: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

答案 2 :(得分:0)

当您将某些内容放入std::vector时,vector会存储副本。操作原件将不会影响副本。如果将指针放入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);

可以减轻工作量。

不幸的是,您使用list3的实验失败了,因为虽然list3是指针vec但没有指针,因此list3被取消引用而vector被引用被复制了。不存储指向数据list3引用的指针,vec包含空向量,原因与上述相同。

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

关于在vector s

中存储指针的一些注意事项
  1. vector仅存储指针的副本。指向的数据必须以在vector完成之前不会被销毁的方式确定范围。一种解决方案是动态分配存储。
  2. (这通常适用于动态分配的指针)如果你动态分配,迟早有人必须清理混乱和delete那些指针。查看storing smart pointers而不是原始指针,而不是存储指针。
  3. 熟悉the Rule of Threevector照顾自己,但是如果你有vector的两个副本,并且只删除并删除其中一个指针,那么你将需要做一些调试。