【问题标题】:Understanding the life time of an object, scope, RAII了解对象、范围、RAII 的生命周期
【发布时间】:2011-03-19 21:05:52
【问题描述】:

在below code中,当我将一个未命名的A变量传递给B的ctor时,该变量在行后被破坏。根据this answer:

临时对象在 他们的完整表达结束 部分。一个完整的表达式是 不是子表达式的表达式 的某种其他表达方式。通常这个 表示它在; (or ) for if, while, switch etc.)结束,表示结束 声明。

我明白了,但是 B 类在被破坏后如何​​知道其 mamber_a 变量的值?我知道A 的复制ctor 被调用。这怎么可能?

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

输出是:

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

【问题讨论】:

    标签: c++ scope raii


    【解决方案1】:

    你有 C++ 的一个棘手部分

    member_a 的值为 20 纯属偶然。您遇到了所谓的未定义行为。

    当一个类保留对外部对象的引用时,程序员有责任确保对象的生命周期比被引用的对象长。在这种情况下,当您调用 member_a.WriteA(); 时,a 对象已经被销毁,因此您正在访问可能不属于您的内存(在这种情况下,它恰好位于附近尚未被覆盖的位置(完全由机会))。

    如果您要保留对对象的引用。您可以保留 const 引用,但有时最好将参数设为普通引用,这样您就不会意外传递临时值(这并不总是有效,因为您可能必须通过引用传递 const 对象)。

    【讨论】:

      【解决方案2】:

      使用对被破坏对象的引用是一种“未定义的行为”。

      在 A 的析构函数中,尝试将“sign”设置为“-1”,看看会发生什么。很可能对“WriteB”的调用将表明您已向已故对象发送了一条消息。

      现在尝试使用代码在 b2 的构造函数和对 b2.WriteB 的调用之间放置一堆堆栈,例如对子例程的调用。您现在可能会发现调用打印的内容有所不同。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-28
        • 2019-11-27
        • 1970-01-01
        相关资源
        最近更新 更多