1

考虑:

    class MyObject{
    public:
     MyObject();
     MyObject(int,int);
     int x;
     int y;
     MyObject operator =(MyObject rhs);
    };
    MyObject::MyObject(int xp, int yp){
     x = xp;
     y = yp;
    }
    MyObject MyObject::operator =(MyObject rhs){
     MyObject temp;
     temp.x = rhs.x;
     temp.y = rhs.y;
     return temp;
    }

    int main(){
     MyObject one(1,1);
     MyObject two(2,2);
     MyObject three(3,3);
     one = two = three;
     cout << one.x << ", " << one.y;
     cout << two.x << ", " << two.y;
     cout << three.x << ", " << three.y;

    }

通过这样做,一、二和三中的变量 x 和 y 不变。我知道我应该更新 MyObject 的成员变量并使用按引用返回并返回 *this 以获得正确的行为。但是, one = two = three 中的返回值实际发生了什么?return temp 实际上在链中的哪个位置结束,例如一步一步?

4

1 回答 1

4

调用赋值运算符

two = three

返回一个临时对象作为右值。这是类型MyObject并传递给赋值运算符的下一个调用

one = t

(我t用来指代临时对象。)

不幸的是,这不会编译,因为赋值运算符需要一个引用MyObject&,而不是类型的右值MyObject

(由于各种原因,您的代码也无法编译,包括大写Class和拼写错误。)

但是,如果您要定义一个采用右值的赋值运算符(即按值、常量引用或MyObject&&如果使用 C++11 时实际上按右值引用采用参数),则调用将起作用并且临时对象将是复制到函数中。在内部,将进行分配并返回另一个临时对象。

然后最终的临时对象将超出范围,即不再存在。将无法访问其内容。


感谢 Joachim Pileborg 和 Benjamin Lindley 的有益评论。


回答更多细节的请求:MyObject是一个类类型,C++ 标准包括一个关于类类型临时对象生命周期的完整部分(第 12.2 节)。那里详细介绍了各种复杂的情况,我不会全部解释。但基本概念如下:

  1. C++ 有表达式的概念。表达式与声明和语句一起是构成程序代码的基本单元。例如,函数调用f(a,b,c)是一个表达式,或者像a = b. 表达式可能包含其他表达式:a = f(b,c),嵌套在赋值表达式中的函数调用。C++ 还引入了全表达式的概念。在前面的例子中,c是表达式的一部分,f(b,c)也是 的一部分a = f(b,c),如果它没有嵌套在另一个表达式中,我们说那a = f(b,c)词法上包含的完整表达式c

  2. 该标准定义了可以创建临时对象的各种情况。一种这样的情况是从函数调用中按值返回对象(也称为返回 prvalue,§6.6.3)。

  3. 该标准规定,当包含它的完整表达式已被完全评估时,此类临时对象的生命周期结束:

    [...] 临时对象作为评估完整表达式 (1.9) 的最后一步被销毁,该完整表达式 (从词法上) 包含它们的创建点。[...]

    (注意。标准接着定义了这个规则的几个例外。然而,你的赋值运算符的返回值的情况并不是这样的例外。)

  4. 现在,一个对象(类类型)被销毁是什么意思?这首先意味着它的析构函数被调用(§12.2/3)。这也意味着无法再安全地访问该对象的存储空间。因此,如果您在完整表达式的评估结束之前以某种方式设法将临时对象的地址存储在指针中,那么在评估结束后取消引用该指针通常会导致未定义的行为。

  5. 在实践中,这在许多情况下可能意味着以下内容——我在一个可能的场景中描述了临时对象的整个生命周期:

    1. 为了提供临时存储空间,编译器确保在输入包含完整表达式的函数时分配足够的堆栈空间(这发生在实际评估完整表达式之前)。
    2. 在赋值表达式的求值过程中,会创建临时值。编译器确保调用其构造函数来初始化为其分配的空间。
    3. 然后可以在评估它所属的完整表达式的过程中访问或修改临时的内容。
    4. 当表达式已被完全评估时(在您的情况下,这一时刻对应于包含赋值表达式的行的末尾),临时的析构函数被调用。之后,访问为其分配的内存不再安全,尽管实际上该空间将继续作为当前堆栈帧的一部分,直到完成所有这些操作的函数的评估完成。
  6. But, again, this is only an example of what may happen. The creation of temporaries is in many situations not actually required. The compiler may perform optimizations that mean the temporary is never actually created. In this case, the compiler must nevertheless ensure that it could have been be created, e.g. it must ensure that the required constructors and destructors exist (they may never be called though).

于 2013-02-22T02:43:46.253 回答