20

C++ でメソッドを持つというconstことは、オブジェクトがそのメソッドを介して読み取り専用であることを意味しますが、それ以外の場合でも変更される可能性があることを理解しています。

constただし、このコードは参照を介して (つまり、constメソッドを介して)オブジェクトを変更しているようです。

このコードは C++ で合法ですか?

constもしそうなら:それは型システムの性質を壊していますか?なぜ/なぜしないのですか?

そうでない場合:なぜですか?

注 1: 例を少し編集したので、回答は古い例を参照している可能性があります。

編集 2: どうやら C++11 も必要ないので、その依存関係を削除しました。

#include <iostream>

using namespace std;

struct DoBadThings { int *p; void oops() const { ++*p; } };

struct BreakConst
{
    int n;
    DoBadThings bad;
    BreakConst() { n = 0; bad.p = &n; } 
    void oops() const { bad.oops(); }  // can't change itself... or can it?
};

int main()
{
    const BreakConst bc;
    cout << bc.n << endl;   // 0
    bc.oops();              // O:)
    cout << bc.n << endl;   // 1

    return 0;
}

アップデート:

ラムダをコンストラクターの初期化リストに移行しました。これによりconst BreakConst bc;後で.コンストラクターと呼び出し元は、互いの定義を確認しないとこれを知る方法がありませんが、構築後は未定義の動作になるはずです。bc bc

4

5 に答える 5

12

oops() メソッドは、オブジェクトの定数を変更できません。さらにそれはしません。それを行う匿名関数です。この匿名関数は、オブジェクトのコンテキストではなく、オブジェクトを変更できる main() メソッドのコンテキストにあります。

あなたの無名関数は oops() の this ポインターを変更せず (これは const として定義されているため、変更できません)、この this-pointer から非 const 変数を導出することも決してありません。それ自体には this-pointer はありません。this-pointer を無視して、メイン コンテキストの bc 変数を変更するだけです (クロージャーにパラメーターとして渡されます)。この変数は const ではないため、変更できます。まったく関係のないオブジェクトを変更する無名関数を渡すこともできます。この関数は、それを格納するオブジェクトを変更していることを知りません。

次のように宣言する場合

const BreakConst bc = ...

メイン関数もそれを const オブジェクトとして処理し、変更できませんでした。

編集: つまり、const 属性は、オブジェクトにアクセスする具体的な l-value (参照) にバインドされます。オブジェクト自体にバインドされていません。

于 2012-06-18T06:20:34.007 に答える
3

私はすでに似たようなものを見ました。基本的に、オブジェクトを知らないうちに変更する何かを呼び出すコスト関数を呼び出します。

これも考慮してください:

#include <iostream>
using namespace std;

class B;

class A
{
    friend class B;
    B* pb;
    int val;
public:
    A(B& b); 
    void callinc() const;
    friend ostream& operator<<(ostream& s, const A& a)
    { return s << "A value is " << a.val; }
};

class B
{
    friend class A;
    A* pa;
public:
    void incval() const { ++pa->val; }
};

inline A::A(B& b) :pb(&b), val() { pb->pa = this; }
inline void A::callinc() const { pb->incval(); }


int main()
{
    B b;
    const A a(b);  // EDIT: WAS `A a(b)`
    cout << a << endl;
    a.callinc();
    cout << a << endl;
}

これは C++11 ではありませんが、同じことを行います。要点は、const が推移的ではないということです。

callinc()それ自体は変わらないしaincval変わらないb。inの代わりにmain宣言することもでき、すべてが同じようにコンパイルされることに注意してください。const A a(b);A a(b);

これは何十年にもわたって機能しており、サンプルでは同じことをしているだけです。単にクラス B をラムダに置き換えただけです。

編集

コメントを反映するように main() を変更しました。

于 2012-06-18T06:31:17.180 に答える
3

const 参照を使用してオブジェクトを変更しないため、コードは正しいです。ラムダ関数はまったく異なる参照を使用しますが、たまたま同じオブジェクトを指しています。

一般に、C++ の型システムは、const オブジェクトまたは const 参照を変更できないことを正式に保証しないため、このようなケースは型システムを破壊しません。ただし、const オブジェクトの変更は未定義の動作です。

[7.1.6.1] から cv-qualifiers :

cv 修飾された型へのポインターまたは参照は、実際に cv 修飾されたオブジェクトを指し示したり参照したりする必要はありませんが、そうであるかのように扱われます。参照されているオブジェクトが非 const オブジェクトであり、他のアクセス パスを介して変更できる場合でも、const 修飾されたアクセス パスを使用してオブジェクトを変更することはできません。

ミュータブルと宣言されたクラス メンバー (7.1.1) を変更できることを除いて、その有効期間中 (3.8) に const オブジェクトを変更しようとすると、未定義の動作が発生します。

于 2012-06-18T06:20:49.713 に答える
2

問題は、論理 const とビットごとの const の 1 つです。コンパイラは、プログラムの論理的な意味について何も知らず、ビット単位の const のみを強制します。論理定数を実装するのはあなた次第です。これは、示されているような場合、ポイントされたメモリが論理的にオブジェクトの一部である場合、コンパイラが許可したとしても、 const 関数でそれを変更することを控える必要があることを意味します(ビット単位のイメージの一部ではないため)オブジェクトの)。これは、オブジェクトのビット単位のイメージの一部がオブジェクトの論理値の一部でない場合 (たとえば、埋め込まれた参照カウントやキャッシュされた値)、それを にするか mutable、または const をキャストすることさえあることを意味する場合もあります。オブジェクトの論理値を変更せずに変更します。

于 2012-06-18T08:10:48.483 に答える
0

const機能は、偶発的な誤用を防ぐのに役立ちます。専用のソフトウェアハッキングを防ぐようには設計されていません。これはプライベートおよび保護されたメンバーシップと同じです。誰かが常にオブジェクトのアドレスを取得し、メモリに沿ってインクリメントしてクラスの内部にアクセスする可能性があります。それを停止する方法はありません。

だから、はい、あなたはconstを回避することができます。他に何もなければ、単にメモリレベルでオブジェクトを変更できますが、これはconstが壊れていることを意味するものではありません。

于 2012-06-18T08:56:26.427 に答える