逻辑操作符 && || !,在C语言中是经常使用的,C++是完全兼容了C的语法,那么又为什么说有陷阱呢?首先看逻辑操作符的原义:操作数只有两种值,真或假;逻辑表达式不需要全部计算完就能确定表达式的值;最终结果只能是真或假。
int a = 0;
int b = 1;
if(a && b)
{
...
...
}
当我们确定了表达式a的值后就能判断出逻辑表达式为为假,这就是所谓的短路法则,同理表达式(b || a)也不需要计算所有表达式就能确定逻辑表达式的值。目前看来貌似并没有什么问题,那么陷阱从何而来呢?
前面对那么多操作符都进行过重载,那么我们对逻辑操作符进行重载是否可行呢?如果可行,有什么意义呢?先试试吧。
class Test
{
int mValue;
public:
Test(int v)
{
mValue = v;
}
int value() const
{
return mValue;
}
};
bool operator && (const Test& l, const Test& r)
{
return l.value() && r.value();
}
bool operator || (const Test& l, const Test& r)
{
return l.value() || r.value();
}
Test func(Test i)
{
cout << "Test func(Test i) : i.value() = " << i.value() << endl;
return i;
}
int main()
{
Test t0(0);
Test t1(1);
/**根据逻辑操作符来看,func(t0)计算后就能知道逻辑表达式的值**/
/**逻辑操作符的原义是遵守短路规则的*/
if( func(t0) && func(t1) )
cout << "Result is true!" << endl;
else
cout << "Result is false!" << endl;
cout << endl;
/**根据逻辑操作符来看,func(t1)计算后就能知道逻辑表达式的值*/
/**逻辑操作符的原义是遵守短路规则的*/
if( func(1) || func(0) )
cout << "Result is true!" << endl;
else
cout << "Result is false!" << endl;
return 0;
}
首先定义一个类,含有一个私有成员变量,两个成员函数。接着就是对逻辑操作符进行重载,函数返回值是bool,因为逻辑表达式结果只有真或假,函数参数是Test类的两个const引用。返回值就是两个类对象的成员变量之间进行逻辑与或。最后还定义了一个测试函数,用于返回函数执行信息,main中定义了两个对象,分别用0和1初始化。接着就测试重载后的逻辑操作符。
下面看一下输出结果:
Test func(Test i) : i.value() = 1
Test func(Test i) : i.value() = 0
Result is false!
Test func(Test i) : i.value() = 0
Test func(Test i) : i.value() = 1
Result is true!
吃惊了吗?不应该将两条语句都打印出来啊,这样不就不遵守短路规则了吗?是不是有点疑惑?那么我们再来使用函数调用的方式使用重载后的逻辑操作符吧。
if( operator &&(func(t0),func(t1)) )
{
...
}
if( operator || (func(t1),func(t0)) )
{
...
}
但是我们根据输出结果发现也是一样的,虽然结果还是一样的,但是我们似乎可以看出点什么了,它的本质是函数啊,函数在执行函数体之前应该干什么?当然是计算实参啊,不得把参数都给计算了才执行函数体吗?并且计算次序还是不确定的,这样似乎就解释了为什么每个逻辑表达式不遵守短路规则而会打印两条语句了。
那么结论来了:
C++从语法上支持逻辑操作符重载
由于重载逻辑操作符后改变了逻辑操作符本身的原义(不遵守短路规则),所以我们不建议对逻辑重载符进行重载。
实际工程开发中避免对逻辑操作符进行重载
可以通过重载比较操作符代替重载逻辑操作符,使用表达式的值和true或flase进行比较
可以直接使用成员函数代替逻辑操作符的重载
如果非要进行逻辑操作符重载的话可以使用全局函数对其进行重载
这真的是一个大大的陷阱!!!