初心者の C++ プログラマーである私にとって、まだ非常にわかりにくい構造がいくつかあります。その 1 つが ですconst
。 は、非常に多くの場所で、非常に多くの異なる効果で使用できるため、初心者が生き残ることはほぼ不可能です。 C++ の第一人者が、さまざまな使用法と、それらを使用するべきかどうか、また使用しない理由を永遠に説明してくれませんか?
ベストアンサー1
いくつかの用途を収集しようとしています:
いくつかの一時オブジェクトを定数参照にバインドして、その有効期間を延ばします。参照はベースにすることができ、そのデストラクタは仮想である必要はありません。適切なデストラクタが呼び出されます。
ScopeGuard const& guard = MakeGuard(&cleanUpFunction);
説明、コードを使用します:
struct ScopeGuard {
~ScopeGuard() { } // not virtual
};
template<typename T> struct Derived : ScopeGuard {
T t;
Derived(T t):t(t) { }
~Derived() {
t(); // call function
}
};
template<typename T> Derived<T> MakeGuard(T t) { return Derived<T>(t); }
このトリックは、Alexandrescu の ScopeGuard ユーティリティ クラスで使用されています。一時オブジェクトがスコープ外になると、Derived のデストラクタが正しく呼び出されます。上記のコードでは細かい点がいくつか抜けていますが、それが重要な点です。
他のメソッドがこのオブジェクトの論理状態を変更しないことを伝えるには、const を使用します。
struct SmartPtr {
int getCopies() const { return mCopiesMade; }
};
コピーオンライトクラスにはconstを使用するコンパイラが、いつコピーする必要があるか、いつコピーする必要がないかを判断するのに役立つようにします。
struct MyString {
char * getData() { /* copy: caller might write */ return mData; }
char const* getData() const { return mData; }
};
説明: 何かをコピーするときに、元のオブジェクトとコピーされたオブジェクトのデータが同じである限り、データを共有したい場合があります。ただし、オブジェクトの1つが変更されると、元のオブジェクトとコピーの2つのバージョンが必要になります。つまり、コピーに書くどちらのオブジェクトにも、それぞれ独自のバージョンが存在することになります。
コードの使用:
int main() {
string const a = "1234";
string const b = a;
// outputs the same address for COW strings
cout << (void*)&a[0] << ", " << (void*)&b[0];
}
上記のスニペットは、使用されている C++ ライブラリがコピーオンライトを実装しているため、GCC で同じアドレスを出力しますstd::string
。 両方の文字列は、別々のオブジェクトであるにもかかわらず、文字列データ用に同じメモリを共有します。 をb
非 const にすると、 の非 const バージョンが優先されoperator[]
、GCC はバッキング メモリ バッファのコピーを作成します。これは、変更される可能性があり、 のデータに影響を与えてはならないためですa
。
int main() {
string const a = "1234";
string b = a;
// outputs different addresses!
cout << (void*)&a[0] << ", " << (void*)&b[0];
}
コピーコンストラクタがconstオブジェクトと一時オブジェクトからコピーを作成する場合:
struct MyClass {
MyClass(MyClass const& that) { /* make copy of that */ }
};
簡単に変更できない定数を作成するため
double const PI = 3.1415;
任意のオブジェクトを値ではなく参照で渡す場合- コストがかかったり不可能になる可能性のある値渡しを防ぐため
void PrintIt(Object const& obj) {
// ...
}