2

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

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

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 复制回 a 然后退出后, b.pChar 的值变得非法。

据我了解,b 从 a 复制向量,然后 a 又从 b 复制回来。因为我在这个副本之后设置了 pChar 的值,所以它“应该”总是指向某个东西。但是,它的行为就像向量正在被破坏。

这是怎么回事?

4

3 回答 3

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;
};
于 2013-06-14T07:13:09.523 回答
3

你的班级不遵守三分法。您有一个自定义复制构造函数,但没有自定义赋值运算符(也没有自定义析构函数,但这个特定的类可能不需要它)。这意味着foo b = a;你想要什么(调用你的复制ctor),但a = b;没有(调用默认分配操作)。

于 2013-06-14T07:12:57.640 回答
2

正如其他两个答案中提到的,在赋值a = b之后,a.pChar指向b.vec,并且由于b已经离开范围,所以它是一个悬空指针。您应该遵守三规则(使用 C++11 的移动操作的五规则),或者通过避免存储 来使该规则变得不必要pChar,因为这似乎只是 的方便别名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()将始终与 一致nOffset,并且可以将特殊成员默认(或完全省略)。

于 2013-06-14T07:24:01.043 回答