欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

操作符重载(三)

程序员文章站 2022-12-22 11:26:46
[TOC] 1. 结论 前面两次笔记都是C++中可以重载且无副作用的操作符,本次笔记比较特殊,主要列出两个C++语法允许重载、但在工程中不应该(不允许)重载的操作符: 逻辑操作符 和 逗号操作符 构成的逗号表达式 这两个操作符在工程中不允许重载的原因是:重载后无法完全实现操作符的原生语义。 2. 逻 ......

目录

1. 结论

前面两次笔记都是c++中可以重载且无副作用的操作符,本次笔记比较特殊,主要列出两个c++语法允许重载、但在工程中不应该(不允许)重载的操作符:

  • 逻辑操作符 &&||
  • 逗号操作符,构成的逗号表达式

这两个操作符在工程中不允许重载的原因是:重载后无法完全实现操作符的原生语义。

2. 逻辑操作符重载

先来回忆一下逻辑操作符的原生语义

  • 操作数只有true和false两种值
  • 逻辑表达式不需要完全计算就能确定最终结果(短路法则)
  • 最终结果也只能是true或者false

c++语法是允许重载逻辑操作符的,看下面的示例代码

#include <iostream>
#include <string>

using namespace std;

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);

    /*
     * 根据短路法则,期望的正确输出为:
     * test func(test i) : i.value() = 0
     * result is false!
    */
    if( func(t0) && func(t1) )
    {
        cout << "result is true!" << endl;
    }
    else
    {
        cout << "result is false!" << endl;
    }

    cout << endl;

    /*
     * 根据短路法则,期望的正确输出为:
     * test func(test i) : i.value() = 1
     * result is true!
    */
    if( func(1) || func(0) )
    {
        cout << "result is true!" << endl;
    }
    else
    {
        cout << "result is false!" << endl;
    }

    return 0;
}

操作符重载(三)

代码中注释了我们期待的正确输出,但实际运行结果一个都没对上,为什么呢?

  • 操作符重载的本质是通过函数调用扩展操作符的功能,47行和63行的if条件分别等价于operator && (func(t0), func(t1))operator || (func(t1), func(t0))
  • 重载函数在进入函数体之前必须完成所有参数的计算,而函数参数的计算顺序是不确定的
  • 短路法则完全失效,逻辑操作符重载后无法完全实现原生语义

因此,在工程中应当避免重载逻辑操作符,建议采用重载比较操作符或提供成员函数的方式,来代替重载逻辑操作符。

3. 逗号操作符重载

逗号操作符,可以构成逗号表达式,下面是逗号表达式的原生语义

  • 逗号表达式用于将多个子表达式连接为一个表达式,如exp1, exp2, exp3, ... , expn
  • 逗号表达式中前n-1个子表达式可以没有返回值,最后一个子表达式必须有返回值
  • 逗号表达式的最终结果为最后一个子表达式的值
  • 逗号表达式严格按照从左向右的顺序计算每个子表达式的值
#include <iostream>
#include <string>

using namespace std;

void func(int i)
{
    cout << "func() : i = " << i << endl;
}

int main()
{
    int a[3][3] =
    {
        (0, 1, 2),   //逗号表达式,等价于a[3][3] = {2, 5, 8,};
        (3, 4, 5),
        (6, 7, 8)
    };

    int i = 0;
    int j = 0;

    while (i < 5)
        func(i),    //逗号表达式,等价于func(i), i++;

             i++;

    cout << endl;

    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            cout << a[i][j] << endl;
        }
    }

    cout << endl;

    (i, j) = 6;  //逗号表达式,等价于 j = 6;

    cout << "i = " << i << endl;
    cout << "j = " << j << endl;

    return 0;
}

操作符重载(三)

在c++中重载逗号操作符是合法的,重载函数的参数必须有一个是类类型,且重载函数的返回值类型必须是引用

class &operator , (const class &a, const class &b)
{
    return const_cast<class &>(b);
}

看下面的示例代码

#include <iostream>
#include <string>

using namespace std;

class test
{
    int mvalue;
public:
    test(int i)
    {
        mvalue = i;
    }
    int value()
    {
        return mvalue;
    }
};

test &operator , (const test &a, const test &b)
{
    return const_cast<test &>(b);
}

test func(test &i)
{
    cout << "func() : i = " << i.value() << endl;

    return i;
}

int main()
{
    test t0(0);
    test t1(1);

    /*
     * 期望的正确输出为:
     * func() : i = 0
     * func() : i = 1
     * 1
    */
    test tt = (func(t0), func(t1));
    cout << tt.value() << endl;

    return 0;
}

操作符重载(三)

虽然第44行输出结果和预期相符,但第43行输出和预期不符,原因和逻辑操作符重载一样,都是由函数参数计算顺序的不确定性造成的,也就是说:

  • 逗号操作符重载后,构成的逗号表达式无法严格按照从左向右的顺序计算各个子表达式
  • 逗号操作符重载后无法完全实现原生语义
  • 因此,在工程中不要重载逗号操作符