从复制构造函数外部修改对象成员时导致向量内存损坏,但从复制构造函数内部修改时不会


#include <iostream>
#include <vector>
#include <cassert>
class a_class
{
public:
    int num_IN;
    a_class():num_IN(0){}
    a_class(a_class const & origin){/*Initialise();*/}   //if not called here, error occurs
    void Initialise(){num_IN =5;}
};
int main () 
{
    std::vector <a_class> the_vector;
    for(int q=0; q < 30; q++)
    {
        the_vector.push_back(a_class());
        the_vector[q].Initialise();             
        assert(5 == the_vector[q].num_IN);      //no problem here
    }
    for(int q=0; q < 30; q++)
        assert(the_vector[q].num_IN == 5);      //assertion fails
}

我不明白从外部调用和在CC内部调用之间的区别,我也不知道为什么它在任何情况下都会引起问题。

std::vector可能会重新分配它使用的缓冲区,如果它的大小超过它,在这种情况下,它必须将旧元素复制到新的缓冲区。如果没有合适的复制构造函数来复制num_IN,则旧值将丢失。

通过提供一个正确的复制构造函数来修复这个问题:

a_class(a_class const & origin) : num_IN(origin.num_IN) {}

在上面的代码中,复制构造函数甚至是不需要的——如果你不提供,编译器会在这里生成一个合适的。

当您重复调用push_back()以附加新元素时,std::vector类可能必须重新分配底层动态数组。通常的策略是std::vector将底层缓冲区的大小增加一个因子,可能是2的一个因子。

当这种重新分配发生时,将调用复制构造函数(如果使用c++0x定义了一个构造函数,则调用move构造函数)将vector的元素从旧缓冲区复制到新缓冲区。

你的复制构造函数实际上没有正确复制,你应该分配num_IN参数:

a_class(a_class const& src): num_IN(src.num_IN) {}

通常,对于STL容器,存储的数据类型应该遵守"三规则",即构造函数、复制构造函数、赋值操作符和析构函数都可以健壮地一起工作。

c++0x中的移动语义,我想这应该扩展到"五规则",因为你还应该考虑适当定义的移动构造函数和移动赋值操作符。

问题是由于push_back,每当向量的size增加超过其capacity时,您的向量在第一个for循环中重新分配内存。在这个重新分配过程中,vector通过调用复制构造函数复制其中已经存在的对象。由于您的复制构造函数不正确(您没有在那里做任何事情),因此在重新分配期间不会保留初始化的值。

您的复制构造函数没有做任何事情。应该是num_IN = origin。num_IN;

啊哈,

我能看到发生了什么。

在第一个循环中,将对象压回vector容器,每次压回vector容器时,vector容器的大小都会被调整,并对vector容器中的所有元素调用复制构造函数。由于您已经覆盖了复制构造函数而不做任何事情,因此num_IN未初始化。

使它工作。我将重新启用复制构造函数来执行成员式复制。

或者。

i将把向量的大小调整为所需的大小。这样,vector就不会在每次添加额外元素

时重新调整大小。
std::vector <a_class> the_vector;
the_vector.resize(30);
for(int q=0; q < 30; q++)
{
    the_vector[q].Initialise();             
    assert(5 == the_vector[q].num_IN);      //no problem here
}
for(int q=0; q < 30; q++)
    assert(the_vector[q].num_IN == 5);      //assertion fails

最新更新