8

特定のケースで const がどのように適用されるかを正確に理解するのに少し苦労しています。ここに私が持っているコードがあります:

struct Widget
{
    Widget():x(0), y(0), z(0){}

    int x, y, z;
};

struct WidgetHolder //Just a simple struct to hold four Widgets.
{
    WidgetHolder(Widget a, Widget b, Widget c, Widget d): A(a), B(b), C(c), D(d){}

    Widget& A;
    Widget& B;
    Widget& C;
    Widget& D;
};

class Test //This class uses four widgets internally, and must provide access to them externally.
{
    public:
        const WidgetHolder AccessWidgets() const
        {
            //This should return our four widgets, but I don't want anyone messing with them.
            return WidgetHolder(A, B, C, D);
        }

        WidgetHolder AccessWidgets()
        {
            //This should return our four widgets, I don't care if they get changed.
            return WidgetHolder(A, B, C, D);
        }

    private:
        Widget A, B, C, D;
};

int main()
{
    const Test unchangeable;

    unchangeable.AccessWidgets().A.x = 1; //Why does this compile, shouldn't the Widget& be const?
}

基本的に、testというクラスがあります。内部で 4 つのウィジェットを使用しており、これらを返す必要がありますが、test が const と宣言されている場合は、ウィジェットも const を返す必要があります。

main() のコードがコンパイルされる理由を誰かに説明してもらえますか?

どうもありがとうございました。

4

6 に答える 6

7

const Widget& オブジェクトを保持するための新しい型を作成する必要があります。すなわち:


struct ConstWidgetHolder
{
    ConstWidgetHolder(const Widget &a, const Widget &b, const Widget &c, const Widget &d): A(a), B(b), C(c), D(d){}

    const Widget& A;
    const Widget& B;
    const Widget& C;
    const Widget& D;
};

class Test
{
public:
    ConstWidgetHolder AccessWidgets() const
    {
        return ConstWidgetHolder(A, B, C, D);
    }

次のエラーが表示されます (gcc 4.3 の場合)。

widget.cc: 関数 'int main()' 内:
widget.cc:51: エラー: データメンバー 'Widget::x' が読み取り専用構造に割り当てられています

同様のイディオムが、イテレータを持つ標準ライブラリで使用されています。


class vector {
    iterator begin();
    const_iterator begin() const;

于 2008-09-11T19:40:07.137 に答える
3

unchangeable.AccessWidgets():

この時点で、タイプ WidgetHolder の新しいオブジェクトを作成しています。このオブジェクトは const によって保護されていません。

また、Wdiget への参照ではなく、WidgetHolder に新しいウィジェットを作成しています。

于 2008-09-11T19:09:14.857 に答える
3

無効WidgetHolderな参照 (ポインター) を保持することになります。スタック上のオブジェクトをコンストラクターに渡し、それらの (一時的な) アドレスへの参照を保持しています。これは壊れることが保証されています。

参照自体と同じ (またはそれ以上) の有効期間を持つオブジェクトへの参照のみを割り当てる必要があります。

参照を保持する必要がある場合は、参照をコンストラクターに渡します。さらに良いことに、参照をまったく保持せず、コピーを作成するだけです。

于 2008-09-11T19:11:15.007 に答える
2

WidgetHolder は const オブジェクトですが、この const-ness は WidgetHolder が指す (参照される) オブジェクトに自動的に適用されないため、これはコンパイルされます。マシン レベルで考えてみてください。WidgetHolder オブジェクト自体が読み取り専用メモリに保持されている場合でも、WidgetHolder が指すものに書き込むことができます。

問題は次の行にあるようです。

WidgetHolder(Widget a, Widget b, Widget c, Widget d): A(a), B(b), C(c), D(d){}

Frank が述べたように、WidgetHolder クラス内の参照は、コンストラクターが戻った後、無効な参照を保持します。したがって、これを次のように変更する必要があります。

WidgetHolder(Widget &a, Widget &b, Widget &c, Widget &d): A(a), B(b), C(c), D(d){}

それを行った後はコンパイルされません。残りのソリューションを解決するための演習として、読者に残します。

于 2008-09-11T19:14:59.427 に答える
0

元のクエリは、含まれているクラスが const の場合に WidgetHolder を const として返す方法でした。C++ は関数シグネチャの一部として const を使用するため、同じ関数の const バージョンと非 const バージョンを使用できます。none const はインスタンスが none const の場合に呼び出され、const はインスタンスが const の場合に呼び出されます。したがって、解決策は、直接ではなく、関数によってウィジェット ホルダー内のウィジェットにアクセスすることです。元の質問に答えると思われる、より単純な例を以下に作成しました。

#include <stdio.h>

class Test
{
public:
  Test(int v){m_v = v;}
 ~Test(){printf("Destruct value = %d\n",m_v);}

 int& GetV(){printf ("None Const returning %d\n",m_v); return m_v;  }

 const int& GetV() const { printf("Const returning %d\n",m_v); return m_v;}
private:
  int m_v;
};

void main()
{
  // A none const object (or reference) calls the none const functions
  // in preference to the const
  Test one(10);
  int& x = one.GetV();
  // We can change the member variable via the reference
  x = 12;

  const Test two(20);
  // This will call the const version  
  two.GetV();

  // So the below line will not compile
  // int& xx = two.GetV();

  // Where as this will compile
  const int& xx = two.GetV();

  // And then the below line will not compile
  // xx = 3;

}

元のコードに関しては、クラス Test のメンバーとして WidgetHolder を持ち、それへの const または none const 参照を返し、Widgets をホルダーのプライベート メンバーにして、各ウィジェットの const および none const アクセサー。

class WidgetHolder {
...

Widget& GetA();
const Widget& GetA() const;
...
};

そして、メインクラスで

class Test {
...
WigetHolder& AccessWidgets() { return m_Widgets;}
const WidgetHolder&AcessWidgets() const { return m_Widgets;}

private:
  WidgetHolder m_Widgets;
...
};
于 2008-09-15T13:01:28.810 に答える
0

編集:彼は答えを削除したので、私は少しばかげているように見えました:)

Flame の答えは危険なほど間違っています。彼の WidgetHolder は、コンストラクター内の値オブジェクトへの参照を取ります。コンストラクターが戻るとすぐに、その値渡しオブジェクトは破棄されるため、破棄されたオブジェクトへの参照を保持します。

彼のコードを使用した非常に単純なサンプル アプリは、これを明確に示しています。

#include <iostream>

class Widget
{
    int x;
public:
    Widget(int inX) : x(inX){}
    ~Widget() {
    std::cout << "widget " << static_cast< void*>(this) << " destroyed" << std::endl;
     }
};

struct WidgetHolder
{
    Widget& A;

public:
    WidgetHolder(Widget a): A(a) {}

    const Widget& a() const {
    std::cout << "widget " << static_cast< void*>(&A) << " used" << std::endl;
    return A;
}

};

int main(char** argv, int argc)
{
Widget test(7);
WidgetHolder  holder(test);
Widget const & test2 = holder.a();

return 0;
} 

出力は次のようになります

ウィジェット 0xbffff7f8 が破壊されました
ウィジェット 0xbffff7f8 使用
ウィジェット 0xbffff7f4 が破壊されました

これを回避するには、WidgetHolder コンストラクターは、参照として保存する変数への参照を取得する必要があります。

WidgetHolder構造体
{
    ウィジェット&A;

公衆:
    WidgetHolder(ウィジェット & a): A(a) {}

  /* ... */

};
于 2008-09-12T09:08:09.097 に答える