我使用流运算符<<和位移算子<<<在一条线上。 我有点困惑,为什么代码A)不会产生与代码B相同的输出?
A)
int i = 4;
std::cout << i << " " << (i << 1) << std::endl; //4 8
B)
myint m = 4;
std::cout << m << " " << (m << 1) << std::endl; //8 8
类myint:
class myint {
int i;
public:
myint(int ii) {
i = ii;
}
inline myint operator <<(int n){
i = i << n;
return *this;
}
inline operator int(){
return i;
}
};
提前谢谢你
哎呀
答案 0 :(得分:8)
你的第二个例子是未定义的行为。
您已在<<
课程中定义了myint
运算符,就好像它实际上是<<=
一样。执行i << 1
时,i
中的值不会被修改,但是当您执行m << 1
时,m
中的值会被修改。< / p>
在C ++中,对于没有插入序列点的变量进行读取和写入(或多次写入)是未定义的行为,对于它们的参数,函数调用和运算符不是。代码
是不确定的std::cout << m << " " << (m << 1) << std::endl;
将在m
更新m
之前或之后输出第一个m << 1
。实际上,您的代码可能会做一些完全奇怪或崩溃的事情。未定义的行为可以导致任何事情,所以避免它。
为<<
定义myint
运算符的正确方法之一是:
myint operator<< (int n) const
{
return myint(this->i << n);
}
(this->
并非严格必要,只是我在重载运算符时的风格)
答案 1 :(得分:5)
因为int&lt;&lt; X返回一个新的int。 myint&lt;&lt; X修改当前的myint。你的myint&lt;&lt;操作员应该固定做前者。
你第一次获得8的原因是显然m&lt;&lt;在您的实现中首先调用1。实现可以按任何顺序自由执行。
答案 2 :(得分:2)
你的&lt;&lt;运算符实际上是&lt;&lt;&lt; =运算符。如果用
替换该行std::cout << i << " " << (i <<= 1) << std::endl; //8 8
你应该得到8 8。
答案 3 :(得分:2)
由于m
是myInt
,您的第二个示例可以重写为:
std::cout << m << " " << (m.operator<<( 1)) << std::endl;
子表达式m
和(m.operator<<( 1))
的评估顺序是未指定的,所以没有说明m
您将获得{{1}的第1个表达式用于(这是一个简单的m
表达式)。所以你可能得到“4 8”的结果,或者你可能得到“8 8”。
请注意,语句不会导致未定义的行为,因为在修改m
和“读取”之间存在序列点(至少一个函数调用)。但是子表达式的评估顺序是未指定的,因此虽然编译器必须产生结果(它不能崩溃 - 至少不合法),但是不应该说它应该产生哪两个可能的结果。
因此该语句与具有未定义行为的语句一样有用,也就是说它不是非常有用。
答案 4 :(得分:1)
井(m&lt;&lt; 1)在m之前被评估,因此m已经保持8,如在你的运营商&lt;&lt;你覆盖自己的价值。
这是你身边的错误行为,操作员&lt;&lt;应该是const而不是改变你的对象。
答案 5 :(得分:0)
因为myint的<<
运算符修改了它的lhs。因此,在评估m << 1
之后,m实际上将具有值8(而i << 1
仅返回8,但不使i等于8)。由于未指定m<<1
是否在cout << m
之前执行(因为未指定函数或运算符的参数的评估顺序),因此未指定输出是否为{{1} }或8 8
。
答案 6 :(得分:0)
C ++语言没有定义运算符的评估顺序。它只定义了它们的相关性。
由于结果取决于在表达式中评估operator<<
函数的时间,因此结果未定义。
代数operator $
函数应始终为const
并返回一个新对象:
inline myint operator <<(int n) const { // ensure that "this" doesn't change
return i << n; // implicit conversion: call myint::myint(int)
}