このクラスがあったとしましょう
struct BigArray{
int operator[](size_t i)const{return m_data[i];}
int& operator[](size_t i){return m_data[i];}
private:
int m_data[10000000];
};
次に、2 つのインスタンスがあるとします。
BigArray a;
a[0]=1;//initializaation etc
BigArray b=a;
この時点で、この不変式が必要です
assert(a[0]==b[0]);
デフォルトの copy ctor はこの不変性を保証しますが、オブジェクト全体のディープ コピーを犠牲にします。このようなスピードアップを試みるかもしれません
struct BigArray{
BigArray():m_data(new int[10000000]){}
int operator[](size_t i)const{return (*m_data)[i];}
int& operator[](size_t i){return (*m_data)[i];}
private:
shared_ptr<int> m_data;
};
これは、ディープ コピーを作成せずに不変条件にも適合するため、これまでのところすべて問題ありません。この新しい実装を使用して、
b[0]=2;
ここで、これをディープ コピーのケース assert(a[0]!=b[0]); と同じように動作させたいと考えています。しかし、それは失敗します。これを解決するには、わずかな変更が必要です。
struct BigArray{
BigArray():m_data(new int[10000000]){}
int operator[](size_t i)const{return (*m_data)[i];}
int& operator[](size_t i){
if(!m_data.unique()){//"detach"
shared_ptr<int> _tmp(new int[10000000]);
memcpy(_tmp.get(),m_data.get(),10000000);
m_data=_tmp;
}
return (*m_data)[i];
}
private:
shared_ptr<int> m_data;
};
これで、const アクセスのみが必要な場合は浅くコピーされ、非 const アクセスが必要な場合はディープ コピーされるクラスができました。これが、「shared_data」ポインターの概念の背後にある考え方です。const呼び出しはディープ コピーしません (「デタッチ」と呼びます) が、非 const は共有されている場合はディープ コピーします。また、 operator== の上にいくつかのセマンティクスを追加して、ポインターだけでなくデータも比較するようにして、これが機能するようにします。
BigArray b=a;//shallow copy
assert(a==b);//true
b[0]=a[0]+1;//deep copy
b[0]=a[0];//put it back
assert(a==b);//true
この手法は COW (Copy on Write) と呼ばれ、C++ の黎明期から存在しています。また、非常に脆弱です。上記の例は、小さく、ユースケースが少ないため、うまくいくようです。実際には、面倒なことはめったになく、実際、C++0x は COW 文字列を非推奨にしています。したがって、注意して使用してください。