C++运算符重载详解及代码示例

一、什么是运算符重载,解决了什么问题?

运算符重载指的是对已有的运算符,如:+ - * / 等重新进行定义,以适应不同的数据类型进行相应的运算。

为什么要重载运算符,对于操作系统基本的数据类型,如 int, float等,编译器知道如何进行运算。但是对于我们自定义的数据类型,比如我想实现两个结构体或者类的相加。编译器就不知道如何对其进行运算了。这个时候我们就需要重载相应的运算符。

二、运算符重载格式

operator 运算符(args…)

例如,operator+() 重载+运算符
   operator*()重载*运算符

三、运算符重载示例

3.1 “+” 加法运算符重载

因为每个人都可以实现该成员函数,且函数名各不相同,你也可以叫AddStudent。为了统一名称,编译器提供了operator+。

#include <iostream>
using namespace std;

class Student{
public:
	// 3.1 类的成员函数实现+重载
    Student Add(Student& s)   // 我们自己实现的Add
    {
        Student tmp;
        tmp.m_A = this->m_A + s.m_A;
        tmp.m_B = this->m_B + s.m_B;
        return tmp;
    }
    Student operator+(Student& s)   // operator+ 是编译器提供的统一的+运算符重载的名称
    {
        Student tmp;
        tmp.m_A = this->m_A + s.m_A;
        tmp.m_B = this->m_B + s.m_B;
        return tmp;
    }
	
public:
    int m_A;
    int m_B;    
};

// 3.2 全局函数实现+重载
Student operator+(Student &s1, Student &s2)
{
    Student tmp;
    tmp.m_A = s1.m_A + s2.m_A;
    tmp.m_B = s1.m_B + s2.m_B;
    return tmp;
}

int main(int argc, char* argv[])
{
    Student s1, s2;
    s1.m_A = 10;
    s1.m_B = 10;

    s2.m_A = 20;
    s2.m_B = 20;

	std::cout << " =========== Add ========"<< std::endl;
    Student s3 = s1.Add(s2);
    std::cout << "s3.A = "<< s3.m_A << "  B = "<< s3.m_B << std::endl;
	
	std::cout << " =========== operator+ ========" << std::endl;
	// Student s4 = s1.operator+(s2);  // 可简写为:Student s4 = s1 + s2;
	Student s4 = s1 + s2;
	std::cout << "s4.A = "<< s4.m_A << "  B = "<< s4.m_B << std::endl;
    
     std::cout << " =========== operator+(Student &s1, Student &s2) ========" << std::endl;
	// Student s6 = operator+(s1, s2);
    Student s6 = s1 + s2;   // 可简写为:Student s6 = s1 + s2;
	std::cout << "s6.A = "<< s6.m_A << "  B = "<< s6.m_B << std::endl;
	return 0;
}

小结:
对于基本数据类型表达式的运算符无法重载。
不要滥用运算符重载,比如故意将运算符+重载为减法的功能。

3.2 “<<” 左移运算符重载
#include <iostream>
using namespace std;

class Student{
public:

    // 成员函数实现左移运算符
    // void operator<<(Student& s){}  // 不可避免调用形式 s1.operator<<(s) 简化版本 s1 << s
    // void operator<<(cout){}  // 不可避免调用形式 s1.operator<<(cout) 简化版本 s1 << cout,不满足cout在左侧,所以左移运算符只能使用全局函数重载
public:
    int m_A;
    int m_B;    
};

// 改造前,全局函数实现左移运算符重载
void operator<<(ostream& cout, Student& s)
{
    cout << "s.a = " << s.m_A << "  s.b = " << s.m_B;
}

// 改造后,全局函数实现左移运算符重载
ostream& operator<<(ostream& cout, Student& s)
{
    cout << "s.a = " << s.m_A << "  s.b = " << s.m_B;
    return cout;
}

int main(int argc, char* argv[])
{
    Student s1, s2;
    s1.m_A = 10;
    s1.m_B = 10;

    s2.m_A = 20;
    s2.m_B = 20;

    std::cout << "[s1] "<< s1; // 改造前
	std::cout << "[s1] "<< s1 << " [s2] "<< s2 << std::endl; // 改造后,支持
	return 0;
}

运行结果:
改造前:
在这里插入图片描述
改造后:
在这里插入图片描述

注意,此时 std::cout << “p” << s1 ; 我们是没有加换行的,如果加了换行代码(std::cout << “p” << s1 << std::endl;)会报错。为什么呢?因为我们用来实现的左移运算符重载的全局函数的返回值是void。如果要继续输出,我们的返回值就必须还是cout的类型。这就是链式编程思想。

3.3 “++” 自增运算符重载
#include <iostream>
using namespace std;

class Myinteger{
public:
    friend ostream& operator<<(ostream& cout, Myinteger m); // 声明该函数为本类的友元函数,可以访问本类的私有成员

    Myinteger(){m_Num = 0;}

    // 前置++运算符重载,返回值必须是引用
    // 比如:(++(++a)) 是对a加两次,如果返回值不是引用,那么(++(++Myinteger)) 就不是同一个对象
    Myinteger& operator++(){
        m_Num++;
        return *this;
    }

    // 后置++运算符重载  因为同一作用域下,函数返回值不同不能作为函数重载的依据,需要加int占位。
    // 后置++  是先返回值,再运算
    Myinteger operator++(int){
        Myinteger tmp = *this;
        m_Num++;
        return tmp; // 注意,这里的tmp是临时变量,离开作用域会销毁,如果返回值是引用,继续使用该临时对象就是非法操作
    }
private:
    int m_Num;
};


ostream& operator<<(ostream& cout, Myinteger m)
{
    cout << m.m_Num;

    return cout;
}

int main(int argc, char* argv[])
{
    std::cout << "========= int =========" << std::endl;
    int a = 0;
    std::cout << "++a = " << ++a << std::endl;
    std::cout << "a   = " << a << std::endl;
    std::cout << "a++ = " << a++ << std::endl;
    std::cout << "a   = " << a << std::endl;

    std::cout << "========= Myinteger =========" << std::endl;
    Myinteger m;
    std::cout << "++m = " << ++m << std::endl;
    std::cout << "m   = " << m << std::endl;
    std::cout << "m++ = " << m++ << std::endl;
    std::cout << "m   = " << m << std::endl;
	
	return 0;
}

运行结果:
在这里插入图片描述

3.4 “=” 赋值运算符重载
class Person{
public:
    Person(int age)
    {
        m_Age = new int(age); // 在堆上维护一块空间
    }
    ~Person(){ // 在析构函数中释放堆上的空间
        if (m_Age){
            delete m_Age;
            m_Age = nullptr;
        }
    }

    Person& operator=(Person& p){
    	//先判断堆上是否存在旧数据,如果有清除干净
        if (m_Age){
            delete m_Age;
            m_Age = nullptr;
        }
		// 执行深拷贝
        m_Age = new int(*p.m_Age);
		// 返回对象本身,链式编程思想
        return *this;
    }
public:
    int *m_Age;
};

int main(int argc, char* argv[])
{
    Person p1(10);

    std::cout << "p1.age: " << *p1.m_Age << std::endl;

    Person p2(20);
    Person p3(30);
    std::cout << "赋值前 p2.age: " << *p2.m_Age << " p3.age: "<< *p3.m_Age<< std::endl;

    p3 = p2 = p1; // 链式编程

    std::cout << "赋值后 p2.age: " << *p2.m_Age << " p3.age: "<< *p3.m_Age<< std::endl;
	return 0;
}

运行结果:
在这里插入图片描述
编译器默认提供的赋值是简单的值拷贝,浅拷贝缺陷,同一块内存释放两次,造成程序崩溃!

3.5 关系运算符重载(< > == <= )
#include <iostream>

using namespace std;

class Person{
public:
    Person(int val){
        m_a = val;
    }

    bool operator<=(Person& p)
    {
        return m_a <= p.m_a ? true : false;
    }

    bool operator<(Person& p)
    {
        return m_a < p.m_a ? true : false;
    }

    bool operator>(Person& p)
    {
        return m_a > p.m_a ? true : false;
    }

    bool operator==(Person& p)
    {
        return m_a == p.m_a ? true : false;
    }
private:
    int m_a;
};

int main(int argc, char* argv[])
{
    Person p1(20);
    Person p2(30);
    Person p3(30);

    if (p1 < p2){
        std::cout << "p1 < p2" << std::endl;
    }

    if (p2 > p1){
        std::cout << "p2 > p1" << std::endl;
    }

    if (p3 == p2){
        std::cout << "p3 == p2" << std::endl;
    }

    if (p3 <= p2){
        std::cout << "p3 <= p2" << std::endl;
    }

    return 0;
}

运行结果:
在这里插入图片描述

3.6 “()”函数调用运算符重载
  1. 函数调用运算符“()”也可以重载
  2. 重载后的使用方式跟函数调用类似,因此称为仿函数
  3. 仿函数非常灵活,没有固定的写法
class MyPrint{

public:
    void operator()(std::string str){ 
        std::cout << str << std::endl;
    }
};

void MyPrint_Func(std::string str){
    std::cout << str << std::endl;
}

class MyAdd{
public:
    int operator()(int a, int b){ 
        return a+b;
    }

};

int MyAdd_Func(int a, int b){
    return a+b;
}
int main(int argc, char* argv[])
{
    MyPrint print;
    print("hello word!");  // 仿函数,顾名思义跟函数调用类似,都是 返回值 函数名+(形参);
    MyPrint_Func("hello word!"); // 函数调用

    int a = 10, b = 20;
    MyAdd add;
    std::cout << "add res: " << add(a, b) << std::endl; // 仿函数,顾名思义跟函数调用类似,都是 返回值 函数名+(形参);
    std::cout << "MyAdd_Func res: " << MyAdd_Func(a, b) << std::endl; // 函数调用
    
    //匿名对象,顾名思义对象没有显示的名字,且这行代码结束,对象被回收
	std::cout << "匿名对象: " << MyAdd()(20, 30) << std::endl; 
    return 0;
}

运行结果:
在这里插入图片描述

文章参考与<零声教育>的C/C++linux服务期高级架构系统教程学习:

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
回答: C++允许重载任何一个关系运算符,包括 <、>、<=、>=、==等。重载后的关系运算符可以用于比较类的对象,也适用于许多C++内置的数据类型。\[1\]运算符重载的语法是通过定义函数来实现的,这样可以大大缩减代码长度。\[2\]为什么要重载运算符呢?对于操作系统基本的数据类型,编译器知道如何进行运算,但是对于自定义的数据类型,比如结构体或类,编译器就不知道如何对其进行运算了。这时候就需要重载相应的运算符来定义自定义类型的运算行为。\[3\]运算符重载的规则包括:重载的函数必须是类的成员函数或友元函数,不能改变运算符的优先级和结合性,不能改变运算符的操作数个数,不能改变运算符的原有语义等。具体的示例可以参考C++的文档或教程。 #### 引用[.reference_title] - *1* [十个 C++ 运算符重载示例,看完不懂打我...](https://blog.csdn.net/luckydarcy/article/details/121599696)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [c++ 运算符重载](https://blog.csdn.net/weixin_54891898/article/details/120495677)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [c++运算符重载详解代码示例](https://blog.csdn.net/weixin_46935110/article/details/127294153)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值