Understanding the life time of an object, scope, RAII
In the below code, when I pass an unnamed A
variable to the ctor of B
, the variable is destructed after the line. According to this answer :
Temporary objects are destroyed at the end of the full expression they're part of. A full expression is an expression that isn't a sub-expression of some other expression. Usually this means it ends at the
; (or ) for if, while, switch etc.)
denoting the end of the statement.
I get it but how can the class B
know the value of its mamber_a
variable, after it is destructed? I know that copy ctor of A
is enver called. How is this po开发者_如何学Cssible?
#include <iostream>
using namespace std;
class A
{
int sign;
A();
const A & operator=(const A &);
public:
A(int x) : sign(x) {
cout << "A ctor : " << sign << endl;
}
void WriteA() const {
cout << sign << endl;
}
~A() {
cout << "A dtor : " << sign << endl;
}
A(const A &) {
cout << "A copied : " << sign << endl;
}
};
class B
{
int sign;
const A & member_a;
public:
B(const A & aa , int ww ) : sign (ww) ,member_a(aa) {
cout << "B ctor : " << sign << endl;
}
void WriteB() const {
cout << "Value of member_a :";
member_a.WriteA();
}
~B() {
cout << "B dtor : " << sign << endl;
}
};
int main() {
A a(10);
B b1(a,1);
b1.WriteB();
B b2(A(20),2);
b2.WriteB();
return 0;
}
The output is :
A ctor : 10
B ctor : 1
Value of member_a :10
A ctor : 20
B ctor : 2
A dtor : 20
Value of member_a :20 // Object A was destructed. Where does this 20 come from?
B dtor : 2
B dtor : 1
A dtor : 10
You have one of the tricky parts of C++
It is pure chance that member_a has the value 20. You are hitting what is refereed to as undefined behavior.
When a class retains a reference to an external object it is the responsibility of the programmer to make sure that the lifetime of the object lasts longer than the object being referred to. In this case when you call member_a.WriteA();
the a object has already been destroyed and thus you are accessing memory that may potentially not belong to you (in this case it just happens to be in a location nearby that has not been overridden (completely by chance)).
If you are going to retain a reference to an object. You can retain a const reference but sometimes it is best to make the parameter a normal reference so that you can not accidentally pass a temporary value (this does not always work as you may have to pass a const object by reference).
Using a reference to a destructed object is an "undefined behaviour".
In A's destructor, try setting "sign" to "-1" and see what happens. Odds are the call to "WriteB" will show that you have sent a message to a deceased object.
Now try putting a bunch of stack using code between the constructor of b2 and the call to b2.WriteB, for example a call to a subroutine. You will probably now find that the call prints something different.
精彩评论