使用std :: vector的不寻常行为

时间:2013-06-14 07:01:36

标签: c++ xml visual-studio-2010 vector stl

我编写了一组相当复杂的类来处理对流(字符串,文件或内存)的迭代。这些不是标准流,也不是相关的。无论如何,由于迭代这些缓冲区并根据缓冲区执行操作,我希望能够在调试器中看到当前的缓冲区位置。因此,仅出于调试原因,我将整个流复制到向量并保持指向此向量中位置的指针。

以下代码的作用类似于前向迭代器。我需要能够存储一个位置,然后使用它或更新它。请注意,此代码仅用于复制问题。

class foo
{
public:
    foo ( std::string szTemp )
        : nOffset( 0 )
    {
        vec.resize( szTemp.size() );
        std::memcpy( &vec[ 0 ], szTemp.c_str(), szTemp.size() );
        pChar = &vec[ 0 ];
    }
    foo( const foo & Other )
        : vec( Other.vec )
        , nOffset( Other.nOffset )
    {
        pChar = &vec[ nOffset ];
    }

    void Inc ( void )
    {
        ++nOffset;
        pChar = &vec[ nOffset ];
    }

    size_t nOffset;
    char * pChar;
    std::vector< char > vec;
};

int _tmain ( int argc, _TCHAR* argv[] )
{
    foo a( "This is a temp string" );

    a.Inc();
    {
        foo b = a;
        b.Inc();
        a = b;
    } // Here is where the problem is seen
    a.Inc();
}

问题,在将b复制回然后踩到后,b.pChar的值变为非法。

据我所知,b从a复制矢量,然后又从b复制它。因为我在这个副本之后设置了pChar的值,它应该总是指向某个东西。但是,它的行为就像矢量被摧毁一样。

发生了什么事?

3 个答案:

答案 0 :(得分:6)

{
    foo b = a;
    b.Inc();
    a = b;
} // Here is where the problem is seen

你的问题就在这里。

a = b;

请求调用赋值运算符,表示您没有实现,因此,默认值将执行成员赋值。在此之后,你的char pointer将是悬空指针。以下代码将做正确的工作。

class foo
{
public:
    foo ( std::string szTemp )
        : nOffset( 0 )
    {
        vec.resize( szTemp.size() );
        std::memcpy( &vec[ 0 ], szTemp.c_str(), szTemp.size() );
        pChar = &vec[ 0 ];
    }
    foo( const foo & Other )
        : vec( Other.vec )
        , nOffset( Other.nOffset )
    {
        pChar = &vec[ nOffset ];
    }
    foo& operator = (const foo& other)
    {
       foo tmp(other);
       swap(tmp);
       return *this;
    }

    void Inc ( void )
    {
        ++nOffset;
        pChar = &vec[ nOffset ];
    }

    void Swap(foo& other)
    {
       std::swap(vec, other.vec);
       std::swap(nOffset, other.nOffset);
       std::swap(pChar, other.pChar);
    }

    size_t nOffset;
    char * pChar;
    std::vector< char > vec;
};

答案 1 :(得分:3)

您的课程不遵循三人规则。您有自定义复制构造函数,但不是自定义赋值运算符(并且没有自定义析构函数,但此特定类可能不需要它)。这意味着foo b = a;执行您想要的操作(调用您的副本ctor),但a = b;没有(调用默认赋值操作)。

答案 2 :(得分:2)

正如其他两个答案中所提到的,在他分配a = b后,a.pChar指向b.vec,并且由于b已离开范围,因此它是一个悬空指针。 您应该遵守三条规则(使用C ++ 11的移动操作的五条规则),或者通过避免存储pChar来使该规则变得不必要,因为这似乎只是{{1}的便利别名}}:

offset

这样,class foo { public: foo ( std::string szTemp ) : nOffset( 0 ) { vec.resize( szTemp.size() ); std::coyp( begin(szTemp), end(szTemp), begin(vec) ); } //special members: foo(foo const&) = default; foo(foo&&) = default; foo& operator=(foo const&) = default; foo& operator=(foo&&) = default; ~foo() = default; void Inc ( void ) { ++nOffset; } char* pChar() //function instead of member variable! { return &vec[nOffset]; } size_t nOffset; std::vector< char > vec; }; 将始终与pChar()保持一致,特殊成员可能只是默认(或完全省略)。