Passing a reference to a C++ constructor

夙愿已清 提交于 2020-01-04 05:26:12

问题


I have this code:

#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;
}

I keep finding myself forgetting the & in the constructor of my classes of this format.

This outputs the following:

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

Why is the behaviour different in the case of y and z?

And why is it not an error to initialize the X &_x member with an instance of type X in the constructor of Y?


回答1:


A copy of the object is created when the constructor is called, hence the different output for z.x. The lifespan of that object is very limited - it exists only in the constructor. This is undefined behavior and the reference will be come invalid.

To prevent such behavior in your applications it is a good practice to mark the copy constructor and assignment operator private.




回答2:


Your code is close to having Undefined Behavior: You are binding a reference to an object (x) that goes out of scope when the constructor of Z returns, which makes it a dangling reference. You do not try to dereference it later on, which is why UB does not show up. But try to read out the value of z.x rather than taking its address, for instance, would be UB.

The reason why it is not an error to assign the reference is that lvalue references are allowed to bind to lvalues, and x is an lvalue. The compiler does not need to determine whether you are going to access _x even after Z's constructor returns (in fact, I'm afraid this is impossible in the general case).

However, a decent compiler should at least issue a warning when you try to bind a reference to a local object: Try to compile with the /Wall option and you should get one.

About the difference in output between y.x and z.x: well, you are printing the addresses of those variables, not their values, and references are just aliases for the variables they reference. Thus, taking the address of a reference yields the same result as taking the address of the variable it is bound to.

And in the case of z, the reference z.x is not bound to the variable x you declared in main() (unlike y.x), but is (was, actually) bound to an object that has gone out of scope and held the argument of Z's constructor (passing by value creates a copy of the argument, and z's reference is an alias for that copy). Thus, a different address is returned by the & operator.




回答3:


Why is the behaviour different in the case of y and z?

Because Y takes the parameter by reference, Z doesn't. Y operates on the original object, Z operates on a copy.

And why is it not an error to initialize the X &_x member with an instance of type X in the constructor of Y?

A diagnostic is not mandatory. Beyond the scope of the constructor, the reference Z::_x is invalid. So accessing it is invalid (however, accessing _x inside the constructor is okay).



来源:https://stackoverflow.com/questions/14730156/passing-a-reference-to-a-c-constructor

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!