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?另外,这是复制包含对象的矢量的最佳方法吗?我想深刻复制。
答案 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 ++。
另一种方法是将vec
和newVec
更改为std::vector<Example>
,以避开原始指针问题。这将是用C ++表达代码的首选方式。
在某些情况下,如果你做多态,你需要指针。然后,您可以使用std::unique_ptr
或std::shared_ptr
来指定语义和RAII。
答案 2 :(得分:2)
Example newE(*(vec[i]));
newVec.push_back(&newE);
在堆栈上分配newE
,然后将指针推送到向量中。在循环迭代结束时,newE
超出了范围,你很幸运,newE
的位置没有被重用。似乎编译器生成的代码将两个堆栈实例放在同一个位置,因此您用第二个值覆盖该位置,然后在迭代指针的向量时,将该位置取消引用两次