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

简介: 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服务期高级架构系统教程学习:

相关文章
|
4月前
|
C++
C++ 语言异常处理实战:在编程潮流中坚守稳定,开启代码可靠之旅
【8月更文挑战第22天】C++的异常处理机制是确保程序稳定的关键特性。它允许程序在遇到错误时优雅地响应而非直接崩溃。通过`throw`抛出异常,并用`catch`捕获处理,可使程序控制流跳转至错误处理代码。例如,在进行除法运算或文件读取时,若发生除数为零或文件无法打开等错误,则可通过抛出异常并在调用处捕获来妥善处理这些情况。恰当使用异常处理能显著提升程序的健壮性和维护性。
84 2
|
4月前
|
算法框架/工具 C++ Python
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
359 0
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
106 5
|
1月前
|
算法 安全 C++
提高C/C++代码的可读性
提高C/C++代码的可读性
59 4
|
2月前
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
396 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
3月前
|
C++
C++(十五) 运算符重载
C++中的运算符重载允许对已有运算符的功能进行重新定义,从而扩展语言功能、简化代码并提升效率。重载遵循特定语法,如 `friend 类名 operator 运算符(参数)`。重载时需注意不可新增或改变运算符数量、语义、优先级、结合性和返回类型。常见示例包括双目运算符 `+=` 和单目运算符 `-` 及 `++`。输入输出流运算符 `&lt;&lt;` 和 `&gt;&gt;` 也可重载。部分运算符只能作为成员函数重载。
|
3月前
|
C++
继续更新完善:C++ 结构体代码转MASM32代码
继续更新完善:C++ 结构体代码转MASM32代码
|
3月前
|
C++ Windows
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
|
3月前
|
C++
2合1,整合C++类(Class)代码转换为MASM32代码的平台
2合1,整合C++类(Class)代码转换为MASM32代码的平台
|
3月前
|
前端开发 C++ Windows
C++生成QML代码与QML里面集成QWidget
这篇文章介绍了如何在C++中生成QML代码,以及如何在QML中集成QWidget,包括使用Qt Widgets嵌入到QML界面中的技术示例。