2

私はこのコードを持っています:

#include <iostream>
using namespace std;

struct X {
    int a = 1;
};

struct Y {
    X &_x;
    Y(X &x) : _x(x) {}
};

// intentionally typoed version of Y, without the reference in the constructor
struct Z {
    X &_x;
    Z(X x) : _x(x) {}
};

int main() {
    X x;
    Y y(x);
    Z z(x);
    cout << "x:   " << &x << endl;
    cout << "y.x: " << &y._x << endl;
    cout << "z.x: " << &z._x << endl;
}

&この形式のクラスのコンストラクターでを忘れていることに気づき続けています。

これにより、次のように出力されます。

x:   0xbfa195f8
y.x: 0xbfa195f8
z.x: 0xbfa195fc

yとの場合の動作が異なるのはなぜzですか?

そして、なぜコンストラクターのX &_x型のインスタンスでメンバーを初期化するのがエラーではないのですか?XY

4

3 に答える 3

4

あなたのコードは未定義の動作に近づいています: オブジェクトへの参照をバインドしています ( x) のコンストラクターが返されたときにスコープ外にZなるため、ダングリング参照になります。後で逆参照しようとしないため、UB は表示されません。ただしz.x、アドレスを取得するのではなく、値を読み取るようにしてください。たとえば、UB になります。

参照を割り当ててもエラーにならない理由は、左辺値参照が左辺値にバインドでき、x左辺値であるためです。のコンストラクターが戻った後_xでも、アクセスするかどうかをコンパイラーが判断する必要はありません(実際、これは一般的なケースでは不可能だと思います)。 Z

ただし、適切なコンパイラは、ローカル オブジェクトへの参照をバインドしようとすると、少なくとも警告を発行する/Wall必要があります。オプションを使用してコンパイルを試みてください。

y.xとの出力の違いについてz.x: まあ、これらの変数の値ではなくアドレスを出力しており、参照はそれらが参照する変数の単なるエイリアスです。したがって、参照のアドレスを取得すると、それがバインドされている変数のアドレスを取得するのと同じ結果が得られます。

の場合z、参照は( とは異なり) で宣言しz.xた変数にバインドされませんが、(実際には) スコープ外になり、のコンストラクターの引数を保持しているオブジェクトにバインドされます (値渡し)引数のコピーを作成し、の参照はそのコピーのエイリアスです)。そのため、オペレータは別のアドレスを返します。xmain()y.xZz&

于 2013-02-06T13:24:52.920 に答える
3

y と z で動作が異なるのはなぜですか?

Y参照によってパラメーターを受け取るため、そうでZはありません。Y元のオブジェクトをZ操作し、コピーを操作します。

また、Y のコンストラクターで X &_x メンバーを型 X のインスタンスで初期化してもエラーにならないのはなぜですか?

診断は必須ではありません。コンストラクターの範囲外では、参照Z::_xは無効です。そのため、アクセスは無効です (ただし、_xコンストラクター内でのアクセスは問題ありません)。

于 2013-02-06T13:24:55.160 に答える
2

コンストラクターが呼び出されると、オブジェクトのコピーが作成されるため、 zx の出力が異なります。 そのオブジェクトの寿命は非常に限られています。コンストラクター内にのみ存在します。これは未定義の動作であり、参照は無効になります。

アプリケーションでこのような動作を防ぐには、コピー コンストラクターと代入演算子をプライベートとしてマークすることをお勧めします。

于 2013-02-06T13:24:14.253 に答える