矢量中的push_back是否插入到同一位置?

时间:2017-08-18 09:01:38

标签: c++ pointers vector

class Example
{
    public: int i;
    Example(const Example &e)
    {
        i = e.i;
    }
    Example(int i)
    {
        this->i = i;
    }
};

int main()
{
std::vector<Example*> vec;
std::vector<Example*> newVec;

Example* ex1 = new Example(1);
Example* ex2 = new Example(2);

vec.push_back(ex1);
vec.push_back(ex2);

//newVec = vec; --> This does shallow copy

for(int i=0; i<vec.size(); ++i) // --> Deep copy
{
    Example newE(*(vec[i]));
    newVec.push_back(&newE);
}

for(int i=0; i<newVec.size(); ++i)
{
    std::cout << "\nfoobar" << newVec[i]->i << "\n";
}
}

上面的代码打印两次foobar2。它不应该打印foobar1和foobar2?另外,这是复制包含对象的矢量的最佳方法吗?我想深刻复制。

3 个答案:

答案 0 :(得分:5)

for(int i=0; i<vec.size(); ++i) // --> Deep copy
{
    Example newE(*(vec[i]));
    newVec.push_back(&newE);
}

在此代码中,您将vec[i]复制到Example newE。然后,push_back newE的<{>}地址到newVec向量。然后newE对象超出范围并被销毁,因此最终在newVec内有一个指向垃圾的指针。

如果您想要矢量内容的深层副本,,您希望存储拥有指向对象的指针,请考虑使用智能指针向量,例如vector<shared_ptr<Example>>

在这种情况下,您只需使用operator=复制向量,shared_ptr的引用计数将自动更新。

Yoy可能还想考虑只有vector<Example>(没有指针间接)的简单设计。

答案 1 :(得分:3)

您的代码不正确,导致行为未定义。

这个循环存在问题:

for(int i=0; i<vec.size(); ++i) // --> Deep copy
{
    Example newE(*(vec[i]));
    newVec.push_back(&newE);
}

您声明了一个局部变量newE。这样的变量具有自动存储持续时间(通常称为堆栈分配,尽管C ++标准在技术上不需要使用堆栈来实现它们)。

具有自动存储持续时间的变量具有以下属性:

  • 他们的生命从他们被宣布的那一刻开始。
  • 他们的生命周期结束于结束范围他们被声明。

for循环的每次迭代都是一个范围,因此在第一次迭代之后,newE不再有效,编译器可以自由生成将重用内存的指令。

这正是您的案例(以及任何体面的实现)中发生的事情:每次迭代都会将newE放在完全相同的地址。

但是,当您使用operator&获取newE的地址,然后newE超出范围时,您现在拥有悬空指针

悬挂指针本身并不成问题,但你不能对它们做任何有意义的事情。特别是,解除引用会导致未定义的行为

您有多种方法可以修复代码。

简单的方法是使用new

Example* newE = new Example(*vec[i]);
newVec.push_back(newE);

但是你必须确保添加适当的delete。而且,这不是真正迂腐的C ++。

另一种方法是将vecnewVec更改为std::vector<Example>,以避开原始指针问题。这将是用C ++表达代码的首选方式。

在某些情况下,如果你做多态,你需要指针。然后,您可以使用std::unique_ptrstd::shared_ptr来指定语义和RAII。

答案 2 :(得分:2)

Example newE(*(vec[i]));
newVec.push_back(&newE);

在堆栈上分配newE,然后将指针推送到向量中。在循环迭代结束时,newE超出了范围,你很幸运,newE的位置没有被重用。似乎编译器生成的代码将两个堆栈实例放在同一个位置,因此您用第二个值覆盖该位置,然后在迭代指针的向量时,将该位置取消引用两次