C ++での「const」の使用法はいくつですか。


129

初心者のC ++プログラマーとして、私にはまだ非常に不明瞭に見えるいくつかの構造体がありますconst。これらの1つはです。あなたはそれを非常に多くの場所で使用することができ、初心者が生き返ることがほとんど不可能である非常に多くの異なる効果があります。一部のC ++の達人は、さまざまな使用法、およびそれらを使用しないかどうか、および/または使用しない理由を永久に一度説明しますか?


その質問を正確に探しています:D
アラミン

回答:


100

いくつかの用途を収集しようとしています:

一時的なものをreference-to-constにバインドして、その寿命を延ばします。参照はベースにすることができます-そして、そのデストラクタは仮想である必要はありません-正しいデストラクタはまだ呼ばれています:

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つのバージョンが必要になります。1つは元のバージョン、もう1つはコピーです。つまり、どちらかのオブジェクトへの書き込みコピーして、両方が独自のバージョンを持つようにします。

コードの使用

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 ++ライブラリがcopy-on-writeを実装しているため、私の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) {
    // ...
}

2
例の最初と3番目の使用法について説明していただけますか?
tunnuz、2009年

「呼び出し先にパラメータをNULLにすることはできないことを保証するために」constがその例とどのように関係しているかはわかりません。
Logan Capaldo 2009年

おっと、私は失敗します。どういうわけか参考文献について書き始めた。うめきをありがとうございました:)もちろん、今は削除します:)
Johannes Schaub-litb 2009年

3
最初の例を説明してください。私にはあまり意味がありません。
チクバ、2012年

28

C ++でのconstの主な使用法は2つあります。

定数値

値が、その存続期間中に変更されない(または変更されない)変数、メンバー、またはパラメーターの形式である場合は、constとマークする必要があります。これにより、オブジェクトの変更を防ぐことができます。たとえば、次の関数では、渡されたStudentインスタンスを変更する必要がないため、constとマークしています。

void PrintStudent(const Student& student) {
  cout << student.GetName();
}

なぜそうするのか。基本となるデータが変更できないことがわかっている場合は、アルゴリズムについて推論する方がはるかに簡単です。「const」は役立ちますが、これが達成されることを保証するものではありません。

当然のことながら、データをcoutに出力するのにそれほど多くの考慮は必要ありません。

メンバーメソッドをconstとしてマーク

前の例では、学生をconstとしてマークしました。しかし、C ++は、学生でGetName()メソッドを呼び出してもオブジェクトが変更されないことをどのようにして知りましたか?答えは、メソッドがconstとしてマークされたことです。

class Student {
  public:
    string GetName() const { ... }
};

メソッドを「const」とマークすると、2つのことが行われます。主に、このメソッドはオブジェクトを変更しないことをC ++に伝えます。2つ目は、すべてのメンバー変数がconstとしてマークされているかのように処理されることです。これは役立ちますが、クラスのインスタンスを変更できます。

これは非常に単純な例ですが、うまくいけば、質問への回答に役立つでしょう。


16

これらの4つの宣言の違いを理解するように注意してください。

次の2つの宣言は、意味的に同じです。ccp1とccp2が指す場所は変更できますが、それらが指しているものは変更できません。

const char* ccp1;
char const* ccp2;

次に、ポインターはconstなので、意味をなすには、何かを指すように初期化する必要があります。それを別のものに向けることはできませんが、それが指すものは変更することできます。

char* const cpc = &something_possibly_not_const;

最後に、2つを結合します。つまり、ポイントされているものは変更できず、ポインターは他のどこもポイントできません。

const char* const ccpc = &const_obj;

時計回りのスパイラルルールは、宣言のもつれをほどくのに役立ちますhttp://c-faq.com/decl/spiral.anderson.html


ラウンドアバウトの方法で、そうです!時計回りのスパイラルルールはそれをよりよく説明します。名前(kpPointer)から始めて、トークンを通り抜ける時計回りのスパイラルを描き、各トークンを言います。明らかに、kpPointerの右側には何もありませんが、それでも機能します。
スティーブフォリー

3

ちょっとしたメモとして、ここで読んだように、

const そのすぐ左にあるものすべてに適用されます(そこに何もない場合を除いて、そのすぐ右にあるものすべてに適用されます)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.