使用指针访问obj向量中的第二个元素时的分段错误

时间:2013-10-24 07:52:09

标签: c++

请看一下这段代码:

#include <vector>
#include <iostream>
#include <string>
using namespace std;

class A
{
    private:
        string contentA;

    public:
        A(){ contentA = ""; };
        A( string setContent ){ contentA = setContent; };
        virtual string printContent(){ return contentA; };
};

class B: public A
{
    private:
        string contentB;

    public:
        B( string setContent ){ contentB = setContent; };
        virtual string printContent(){ return contentB; };
};

int main()
{

    vector<A*> aPointer;
    vector<B> bVector;      

    B b1("b1");
    //store b1 obj in bVector
    bVector.push_back( b1 );
    //store the current(last) obj address to aPointer for access later
    aPointer.push_back( &bVector.back() );

//    B b2("b2");
//    bVector.push_back( b2 );
//    aPointer.push_back( &bVector.back() );

    for( vector<A*>::iterator it = aPointer.begin(); it != aPointer.end(); it++ )
    {
        cout << (*it)->printContent() << endl;
    }
}

aPointer将存储指向B向量元素的指针。

我可以问为什么在for循环中访问B矢量中的第二个元素时会出现分段错误?

我已经进入了,它会再次正常工作。

3 个答案:

答案 0 :(得分:6)

您必须记住std::vector的大小可变。向向量添加元素时,可能必须重新分配数据,因此在重新分配后,任何指向向量的指针都将无效。

这里发生的事情可能是这样的重新分配,因此一个指针将无效并且解除引用它将是未定义的行为,并且在您的情况下会导致崩溃。

答案 1 :(得分:2)

问题不在于您访问aPointer的第二个元素时:当您访问第一个元素时。正如您将看到的,当std::vector增长/调整大小并重新分配内存时会导致此问题。当它执行此操作时,引用(指针,迭代器和&引用)将失效,这意味着当您访问这些无效的引用时它会导致未定义的行为(在您的情况下,效果是分段错误)。

让我们看看究竟是什么导致了这个问题:

B b1("b1");
bVector.push_back( b1 );
aPointer.push_back( &bVector.back() );

B b2("b2");
bVector.push_back( b2 );
aPointer.push_back( &bVector.back() );

哦!您正在推回两个元素,其地址在添加时存储。问题出在那里。当您执行第二次push_back时,会导致重新分配和调整大小,从而使对bVector内现有元素的引用无效。并且您在该向量中有一个现有元素,并且已经对其进行了无效的引用!该引用当前是aPointer的第一个元素,它指向一个您不想指向和访问的内存位置。

因此,当您进行迭代时,您将访问aPointer的第一个元素,从而导致分段错误。

要解决您的问题,请不要将指针向量存储到另一个向量的对应元素!你可以只传递整个向量(最好通过引用,这样我们就不会产生复制的代价)并直接对元素本身进行操作。这肯定会为您节省一个小时的头痛,并且会产生更好,更短的代码。

答案 2 :(得分:1)

正如Joachim Pileborg和Mark Garcia都提到的那样,向量容器重新分配内存是个问题。

要解决此问题,您可以使用vector.reserve方法为矢量预先分配内存。虽然这对你的情况有用,但它只是一个创可贴,不应该被视为解决这个问题的 方法。编辑:我想强调我发布这个只是因为它可能有意义。 Joachim和Mark提供了解决根本问题的适当方法。

int main()
{

    vector<A*> aPointer;
    vector<B> bVector;      

    //reserve some space. There's no special meaning to choosing 16.
    aPointer.reserve(16);
    bVector.reserve(16); 

    B b1("b1");
    bVector.push_back( b1 );
    aPointer.push_back( &bVector.back() );

    B b2("b2");
    bVector.push_back( b2 );
    aPointer.push_back( &bVector.back() );

    for( vector<A*>::iterator it = aPointer.begin(); it != aPointer.end(); it++ )
    {
        cout << (*it)->printContent() << endl;
    }
}