3

可能重复:
未定义的行为和序列点

重载后缀运算符时,我无法理解操作的顺序。让我们看看下面的两个小例子:

int i = 0;
std::cout << std::endl << "i: " << i;
i = ++i;
std::cout << std::endl << "i: " << i;
i = i++;
std::cout << std::endl << "i: " << i;

MyClass myObject;
std::cout << std::endl << "myObject: " << myObject.getMyValue();
myObject = ++myObject;
std::cout << std::endl << "myObject: " << myObject.getMyValue();
myObject = myObject++;
std::cout << std::endl << "myObject: " << myObject.getMyValue();

出现了两种截然不同的行为。输出如下:

i: 0
i: 1
i: 2
myObject: 0
myObject: 1
myObject: 1

不同的行为,你看。这是我的重载运算符方法的概要。

MyClass & MyClass::operator++ ()
{
    ++myValue;
    return *this;
}

MyClass MyClass::operator++ (int postfixFlag)
{
    MyClass myTemp(*this);
    ++myValue;
    return myTemp;
}

好吧。前缀是有道理的。您可以增加所需的任何内容,然后返回相同的对象,现在已修改,以防分配。但是后缀是让我绊倒的地方。它应该分配,然后递增。在这里,我们是自我分配的。所以使用内置的整数类型,这是有道理的。我将i' 的值分配给自身,然后i递增。很公平。但是,假设MyClass是对 int 的重新创建。它从 0 开始,前缀递增,然后变为 1。然后是关键行。 myObject = myObject++. 这和myObject = myObject.operator++(int postfixFlag). 它被调用。 myTemp用值 1 初始化。它递增到 2。然后我们返回 temp。如果我们要分配给另一个对象,那就行得通。但在这里我是自分配的,所以在增加到 2 之后,myObject设置为等于返回的用初始值初始化的临时对象,我们又回到了 1!那讲得通。但这是一种根本不同的行为。

我该如何解决它?int 是如何做到的?这个方法一般是怎么写的?您对与此相关的 C++ 行为和设计有什么意见吗?等等我现在有点困惑,因为书籍和在线示例似乎总是使用上述方法的变体。

感谢您的阅读,任何输入将不胜感激!

4

1 回答 1

5

正如其他人所说,使用 int 行为是未定义的。但我想我会尝试解释为什么你的 MyClass 从来没有达到 2。

诀窍是您在后缀版本中采取以下三个步骤:

  1. this制作被调用的副本myTemp(带有myValue == 1)。
  2. 递增this->myValue(所以myTemp.myValue == 1; this->myValue == 2)。
  3. 返回myTemp(带myValue == 1)。

所以你正在修改this,但调用的代码myObject++永远不会再看到this。它只会查看返回的值,它是old myObject的副本。

operator++ 的代码很好。问题是你如何使用它——你不应该将预增量或后增量的结果写回同一个变量(行为未定义)。以下是一些可能更具指导性的代码:

int i = 0;
std::cout << "i: " << i << std::endl;
int j = ++i;
std::cout << "i: " << i << ", j: " << j << std::endl;
int k = i++;
std::cout << "i: " << i << ", k: " << k << std::endl;

MyClass myObject;
std::cout << "myObject: " << myObject.getMyValue() << std::endl;
MyClass myObject1 = ++myObject;
std::cout << "myObject: " << myObject.getMyValue()
    << ", myObject1: " << myObject1.getMyValue() << std::endl;
MyClass myObject2 = myObject++;
std::cout << "myObject: " << myObject.getMyValue()
    << ", myObject2: " << myObject2.getMyValue() << std::endl;

这打印:

i: 0
i: 1, j: 1
i: 2, k: 1
myObject: 0
myObject: 1, myObject1: 1
myObject: 2, myObject2: 1

我更改了您的代码,因此它每次都分配给一个新变量,而不是分配回自身。请注意,在这两种情况下intMyClass主变量 ( i/ myObject) 都会增加两次。但是,在增量前的情况下,新变量 ( j/ myObject1) 采用新值,而在后增量情况下,新变量 ( k/ myObject2) 采用旧值。

编辑:只是回答问题的另一部分,“int 是如何做到的?” 我假设这个问题的意思是“类中的前置增量和后置增量代码是什么样的int,我怎样才能使我的代码相同?” 答案是,没有“int阶级”。int是 C++ 中的一种特殊内置类型,编译器对其进行特殊处理。这些类型不是用普通的 C++ 代码定义的,它们是硬编码到编译器中的。

注意:对于任何想自己尝试的人,这里是MyClass问题不包括的代码:

class MyClass
{
private:
    int myValue;
public:
    MyClass() : myValue(0) {}
    int getMyValue() { return myValue; }
    MyClass& operator++();
    MyClass operator++(int postfixFlag);
};
于 2011-06-29T04:51:40.183 回答