C++.运算符重载

"本文详细介绍了C++中运算符重载的概念和实践,包括如何为自定义类型设计运算符,如字符串类的"+"、"=="等。讨论了单目和双目运算符的成员函数和全局函数实现,以及友元函数在解决访问私有成员问题上的作用。此外,还阐述了输入输出运算符、自变运算符、特殊运算符如下标运算符[]和函数运算符()的重载方式。强调了重载运算符的规则和注意事项,建议保持运算符的一致性和避免过度设计。"
摘要由CSDN通过智能技术生成

一、运算符函数

    在C++中会把运算符当做函数处理,一个表达式,其实是调用了很多的运算符函数完成计算,

    这种特性对于内建类型是没有意义的,但是对于自建类型的数据,可以进行个性化设计,可以大大地提高代码的可读性、易用性

    例如string类

        string s1,s2;

        s1 += s2    strcat()

        s1.cat(s2);

        s1 = s2

        s1 == s2

    运算符函数的格式:[#是运算符 O是类对象]

        单目运算符:  #O  或者 O#  

            成员函数:

            [] O::operator#(void)

            {

            }

            注意:返回值不确定,运算对象就是函数的调用者,没有参数

            全局函数:

            [] operator#(O& o)

            {

                //  全局运算符函数不属于任何类,因此需要把调用者作为参数传递过来

            }

            注意:运算符成员函数、全局函数,只能实现一个,不能同时实现,编译器会产生歧义

       

        双目运算符: a # b

            注意:一定是运算符左边的对象发起的函数调用

            成员函数:

            [] A::operator#(B& b)

            {

                // 类A对象必须是运算符函数的调用者,

            }

            全局函数:

            [] opeartor#(A& a,B& b)

            {

                //函数调用者放第一个参数

            }

    思考:如何实现一个坐标类

    Point p1(1,1), p2(2,2);

    p1 + p2

    (1,1)+(2,2) = (3,3)

   

二、运算类的双目运算符

    class T

    {

        int x;

    };

    成员函数: a + b

    const T T::operator+(const T& b)const

    {

        return T(x+b.x);

    }

    全局函数:

    const T operator+(const T& a,const T& b)

    {

        return T(a.x+b.x);  //x是私有的,无法直接类外访问

    }

    友元:

        在实现类的全局运算符函数时,可能会使用类内的私有成员,此时全局运算符函数是没有访问权限,

        如果把私有成员变成public会打破类的封装性,或者实现get函数会很麻烦,C++提供了友元这种方式

        来为全局的运算符函数提供独家授权,该函数就称为友元函数

        方法:

            在类内对全局运算符函数进行声明,并且在声明前加 friend 关键字即可

三、输入输出运算符

    在C++中 << >> 运算符不光是按位左移、右移,同时它们还是 cout \ cin 的输出、输入运算符

        输出运算符: cout << 10 << endl;

            Test t;

            cout << t << endl;

            此时 << 运算符的调用者是 cout,所以我们无法在cout类中实现 << 运算符函数重载,只能实现全局的<< 运算符函数

        ostream& operator<<(ostream& os,const Test& t)

        {

            return os << t.x;   //不建议换行

        }

        输入运算符: cin >> num;

            Test a,b;

            cin >> a >> b;

        istream& operator>>(istream& is,Test& t)

        {

            return is >> t.x;

        }

        注意:

        1、由于输入、输出运算符是可以连续调用,因此返回值应该还是cin、cout ,应该返回istream& ostream&

        2、由于输入、输出运算符的调用者是左边的cin、cout,我们无法实现它们的成员运算符函数,只能实现全局的输入输出运算符函数

        3、如果在全局运算符函数中使用了私有成员,需要声明全局运算符函数为友元函数

四、运算类的单目运算符

    单目运算符:++/-- ! ~ * & sizeof -

    成员函数:   ! ~ -

    const Test operator-(void)const

    {

        return Test(-x,-y);

    }

    注意:单目运算类的运算符对象都可以带常属性,因此重载的单目运算符函数必须是常函数,

    并且运算过程中都不会改变自身的值,而是产生一个临时的结算结果,并且是右值,只能返回带const的临时对象

    全局函数:

    const Test operator-(const Test& t)

    {

        return Test(-t.x,-t.y);

    }

五、自变运算符函数

    前自变运算符: ++i / --i

    注意: 在C语言中无论前、后自变得到的结果都是右值,但是在C++中前自变的结果是左值,后自变的结果是右值

    int num = 10;

    ++(num++)   C C++ 报错

    (++num)++   C++可以 C报错

    成员函数:

    T& operator++(void)//必须不能是常函数

    {

        x++,y++;

        return *this;   //一定要要返回引用

    }

    全局函数:

    T& operator++(Test& t)//必须不能是常形参

    {

        t.x++,t.y++;

        return t;   //一定要要返回引用

    }

    后自变运算符函数: i++ / i--

        哑元:在参数列表中增加一个不会使用的哑元类型参数,唯一目的就是为了区分前自变还是后自变  (哑元唯一用处)

        成员函数:

        const T operator++(int)

        {

            return T(x++,y++);//必须返回临时对象,不能是引用

        }

        全局函数:

        const T operator++(T& t,int)

        {

            return T(t.x++,t.y++);//必须返回临时对象,不能是引用

        }

        注意:要会写

六、特殊的运算符函数

    1、下标运算符[]

        当想让一个类对象当做数组使用时,可以考虑重载下标运算符,例如vector类中就重载了[]

    2、函数运算符()

        当想让一个类对象当做函数一样使用时,可以考虑重载该运算符

        注意:[] ()只能重载为成员函数,不能重载成全局函数

   

    3、解引用* 和 访问成员运算符->

        重载这两个运算符时可以让一个类对象像指针一样使用

        C++的智能指针就是重载了它们来实现的

    4、new和delete

        为什么要重载new和delete运算符?

            1、可以在重载该运算符函数时记录每次分配、释放内存的地址、时间、行数等信息到日志中,

            可以检查哪里出现了内存泄漏、什么时候出现的

       

        成员函数与全局函数格式一样:

            void* operator new(size_t size)//

            {

                //  size是要申请内存的字节数,编译器会自动计算并传递过来

                //  做一些自己的操作

                void* ptr = malloc(size);

                return ptr;

            }

            void operator delete(void* ptr)

            {

                //  做一些自己的操作

                free(ptr);

            }

            注意:如果只是针对某个类重载它的new\delete,那么只需要实现成员函数即可,

            如果想要所有类型进行new/delete时都是用重载的函数,则实现为全局函数

   

七、重载运算符的规则

    1、有些运算符是不能重载

        ::  域限定符

        .   直接访问成员的运算符

        ? : 三目运算符

        sizeof 计算字节数的运算符

        typeid 获取类型信息的运算符

    2、只能重载成全局函数的运算符

        <<  输出运算符

        >>  输入运算符

    3、只能重载成成员函数的运算符

        []  下标运算符

        ()  函数运算符

        =   赋值操作运算符  类内一定有一个=成员函数

        ->  间接访问成员的运算符

    4、运算符重载可以自定义运算的过程,但是无法改变运算符的优先级

    5、运算符的运算对象数量和格式不能改变

    6、不能发明新的运算符

    建议:

        1、重载运算符要遵循运算符含义一致性,不要改变运算符的运算规则

        2、不要忘记重载运算符的初衷,不要炫技

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaoyu1381

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值