次のことを想像してみてください。C ++ 03を使用していて、次のように記述します。
string a("hello");
string b = a;
この時点で、2つの文字列オブジェクトがa
ありb
、それぞれに文字の配列を格納するための独自のバッファがあります。バッファの内容はまったく同じでa
ありb
、「hello」の独自のコピーがありますが。これはメモリの無駄です。それらがバッファを共有している場合、両方の文字列の「helloworld」を格納するために単一のchar配列を使用する必要があります。
QStringでは、少し異なります。
QString a("Hello");
QString b = a;
この場合、a
「hello」を格納するchar配列のみを作成しました。b
独自のchar配列を作成する代わりに、のchar配列を指すだけa
です。そうすれば、メモリを節約できます。
ここb[0]='M'
で、id estを実行すると、を変更してb
からb
、独自のchar配列を作成し、の配列の内容をコピーしてからa
、独自の配列を変更します。
Javaでは、文字列は不変のオブジェクトです。String
つまり、Javaは、そのコンテンツを変更するためのメソッドをクラスに提供しません。これは、そのようなデータを常に共有できるようにするために作成されました。
他の人が言及したことを補完する:
char配列を解放できることをどうやって知ることができますか?
それが「参照カウント」の目的です。オブジェクトが作成され、char配列を指すように設定されると、その参照カウントが1ずつ増加するため、オブジェクトがまだ使用しているオブジェクトの数がわかります。それを指しているオブジェクトが破棄されると、参照カウントがデクリメントされます。カウンターがゼロに達すると、char配列は誰もそれを使用していないことを認識しているため、解放できます。
これは、参照カウントの非常に大まかな実装です。私はとにかく正確または正しいことを意図していません。C++でコピーコンストラクターと代入演算子を実装する正しい方法を無視しています。実装が機能するかどうかを確認する方法はありません。これは、C++のようなアルゴリズムの記述だと思います。コンセプトを教えたいだけです。しかし、次のクラスがあると想像してください。
class SharedData{
private:
int refcount;
int data;
public:
SharedData(int _data){data=_data;refcount=1;}
void incRef(){refcount++;}
void decRef(){--refcount; if(refCount==0) delete this;}
};
class Data{
SharedData* shared;
public:
Data(int i){shared = new Data(i);}
Data(const Data& data){shared = data.shared; shared->incRef();}
const Data& operator=(const Data& data){if(shared!=data.shared){
shared->decRef();
shared = data.shared;
shared->incRef();}
}
~Data(){shared->decRef();}
};
クラスの2つのオブジェクトは同じオブジェクトData
を共有できるため、次のようになります。SharedData
void someFunction() {
Data a(3) //Creates a SharedData instance and set refcount to 1
if (expression) {
Data b = a; //b points to the same SharedData than a. refcount is 2
b = Data(4);// b points to diferent SharedData. refcount of SharedData of a is decremented to 1 and b's SharedData has refcount 1
//destructor of b is called. Because shared data of b has now refcount == 0, the sharedData is freed;
}
//destructor of a is called, refcount is decremented again
// because it is zero SharedData is freed
}
そのため、リソースの使用量が最大化され、コピーが最小化されました。両方とも同じものa
をb
使用しましたSharedData
(別名int 3
)。。はに4
コピーされませんでした。同じデータを共有しただけですa
。b
intは大したことではありませんが、SharedData
大きな文字列やその他のより複雑なデータ構造を保持している場合を想像してみてください。ポインタだけをコピーする方が、数十バイトよりはるかに高速です。また、実際にコピーが必要ない場合でも、多くのメモリを節約できます。
コピーオンライトとは何ですか?
私たちがやったときに私が言ったことを上で思い出してくださいb[0]='M'
。それはコピーオンライトでした。同じchar配列を共有していましたb
。a
しかしb
、文字列を変更する必要があります。文字列も変更されるため、直接実行することはできませんでしたa
。したがってb
、char配列を変更できるようにするには、char配列の独自のコピーを作成する必要があります。配列を変更するときにのみコピーを作成する必要があるため、コピーオンライトと呼ばれます。