2011-10-19 14 views
3

私はメモリバッファを保持する単純な参照カウントクラスを持っています。これは次のようになります。小さなref-countingバッファクラスのメモリ破損

#include <algorithm> 

template<typename T> 
struct buffer 
{ 
    // create a buffer of length n 
    buffer(unsigned n) : rc(*(new unsigned(1))), data(new T[n]) { } 

    buffer(const buffer<T> & rhs) : rc(++rhs.rc), data(rhs.data) { } 

    buffer<T>& operator=(buffer<T> rhs) 
    { 
     std::swap(rc, rhs.rc); 
     std::swap(data, rhs.data); 
     return *this; 
    } 

    ~buffer() 
    { 
     if (--rc == 0) { 
      delete [] data; 
      delete (&rc); 
     } 
    } 

private: 
    mutable unsigned & rc; 
    T * data; 
}; 


int main() { 
    typedef buffer<int> numbers; 
    numbers n1(10); 
    numbers n2(20); 
    numbers n3(30); 
    n1 = n2 = n3 = n2; 
} 

コードに間違いはありません。しかし、Visual Studioとvalgrindはメモリ破損に苦情を言います。

私はこのコードをあまりにも長く見過ごしています。誰もがエラーを見つけることができますか?

+3

'変更可能な符号なし& rc;' ... 'rc(*(新しい署名なし(1)))'なぜこれをやっていますか? –

+2

これは例外セーフではありません。初期化子リスト内の新しい式のいずれかが失敗した場合、クリーンアップする方法はありません。 –

+0

@JamesMcNellis私は別の何かをするように感じました(それはおもちゃのコードです)。例外的な安全性に関してあなたは正しいです。しかし、私はリークは2番目の 'new'が(配列の)失敗した場合にのみ発生し、' rc'変数に割り当てられたメモリだけがリークすると考えています。 (私はここで例外的で安全でないコードを正当化しようとはしていませんが、結局は間違いです。) – StackedCrooked

答えて

13

1つの問題は、swap(rc, rhs.rc);の場合、実際には参照ではなく、再カウントの内容を入れ替えていることです。あなたswap(rc, rhs.rc);、参照カウントが足さへの参照が同じままになり、そして数自体がスワップするとき

Before

は、あなたがこのような状況を持っている想像してください。これは、2つのスワップ後に取得されるものです:

After

バッファ・ポインタはcorrrectですが、参照カウントが足さへの参照がまだ同じunsignedオブジェクトを参照してください。これらのオブジェクトの値はスワップされました。スワップされたオブジェクトの1つから見たときにバッファBがrefcount 2をどのように持ち、底部から見たときにrefcount 1になるかに注目してください。

refcountにポインタを使用し、内容ではなくポインタをスワップする必要があります。

+6

+1フリーハンドの矢印。 – Maxpm

+0

うわー!私はそれを見たことがないだろう! –

+1

私は今それを得る。私の考えでは、2つの小さな箱が想像され、それぞれにはスレッドが付いています。 2つのボックスの位置を入れ替えると、それに接続されているすべてのスレッドをドラッグします。それが正しい方法です。しかし、私はボックスの内容を交換して、スレッドをそのまま残しました。 – StackedCrooked