类和对象-运算符重载

简介: 类和对象-运算符重载

C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。

重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。

当您调用一个重载函数或重载运算符时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策。

您可以重定义或重载大部分 C++ 内置的运算符。这样,您就能使用自定义类型的运算符。

重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。

运算符重载概念:对已有运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型

加号运算符重载

作用:实现两个自定义数据类型相加的运算

对于内置数据类型,编译器知道如何进行运算:10+10=20

两种实现方法:

  • 成员函数重载运算符
  • 全局函数重载运算符

本质:

  • 成员函数:A c = a.operator+(b)
  • 全局函数:A c = operator+(a, b)
class A
{
public:
    A operator+ (A a)//必须要有加号+
    {
        A temp;
        temp.m_a = this->m_a + a.m_a;
        temp.m_b = this->m_b + a.m_b;
        return temp;
    }
    int m_a = 10;
    int m_b = 10;
};
int main()
{
    A a;
    A b;
    //A c = a.operator+(b);
    A c = a + b;
    cout << c.m_a << endl << c.m_b << endl;
    return 0;
}
class A
{
public:
    int m_a = 10;
    int m_b = 10;
};
A operator+(A a, A b)
{
    A temp;
    temp.m_a = a.m_a + b.m_a;
    temp.m_b = a.m_b + b.m_b;
    return temp;
}
int main()
{
    A a;
    A b;
    //全局重载函数本质调用
    //A c = operator+(a, b);
    A c = a + b;
    cout << c.m_a << endl << c.m_b << endl;
    return 0;
}

运算符重载,也可以发生函数重载:

class A
{
public:
    int m_a = 10;
    int m_b = 10;
};
A operator+(A a, A b)
{
    A temp;
    temp.m_a = a.m_a + b.m_a;
    temp.m_b = a.m_b + b.m_b;
    return temp;
}
A operator+(A a, int b)
{
    A temp;
    temp.m_a = a.m_a + b;
    temp.m_b = a.m_b + b;
    return temp;
}
int main()
{
    A a;
    A b = a + 10;
    cout << b.m_a << endl << b.m_b << endl;
    return 0;
}

总结:

  • 对于内置的数据类型的表达式的运算是不可能修改的
  • 不要滥用运算符重载:加法写成减法,乘法写成除法

左移运算符重载

  • 不会利用成员函数重载<<运算符,因为无法实现cout在左侧
  • 只能利用全局函数重载<<运算符
#include <iostream>
using namespace std;
class Person
{
    //添加友元,以访问私有属性
    friend ostream& operator<<(ostream& cout, Person& person);
private:
    string name = "张三";
    string phone = "110";
};
//cout的数据类型为ostream,返回值类型必须填ostream才能实现链式编程
//使用引用的方式,保证cout只有一个
ostream& operator<<(ostream& cout, Person& person)
{
    cout << person.name << person.phone;
    return cout;
}
int main()
{
    Person a;
    cout << a << endl;//链式编程
    return 0;
}

递增运算符重载

通过重载递增运算符,实现自己的整型数据

注意事项:

  • 前置递增要返回引用
  • 后置递增要返回值,要用int占位参数,让编译器知道是在做后置递增
class Person
{
public:
    //使用引用,为了一直对一个数据进行递增操作
    Person& operator++()//重载前置++
    {
        age += 1;
        return *this;
    }
    //不能用引用,temp局部变量会在运行结束后被销毁,非法访问
    Person operator++(int)//int代表占位参数,可以用于区分前置和后置递增
    {
        Person temp = *this;//先记录当时的结果
        age++;//后递增
        return temp;//最后将记录结果做返回
    }
    int age;
};
ostream& operator<<(ostream& cout, Person a)
{
    cout << a.age;
    return cout;
}
int main()
{
    Person a;
    a.age = 10;
    Person b;
    b.age = 10;
    cout << ++(++a) << endl;//链式编程
    cout << a.age << endl;//检验是否成功递增
    cout << b++ << endl;//链式编程
    cout << b.age << endl;//检验是否成功递增
    return 0;
}

赋值运算符重载

C++编译器会给一个类至少添加4个函数:

  • 默认构造函数(无参,函数体为空)
  • 默认析构函数(无参,函数体为空)
  • 默认拷贝构造函数,对属性进行值拷贝
  • 赋值运算符operator=,对属性进行值拷贝

如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝的问题

#include <iostream>
using namespace std;
class Person
{
public:
    //重载赋值运算符
    Person& operator=(Person a)
    {
        //编译器默认的浅拷贝
        //m_name = a.m_name;
        //应该先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
        if (m_name != NULL)
        {
            delete m_name;
            m_name = NULL;
        }
        //深拷贝
        m_name = new string(*a.m_name);
        //返回自身,以实现连等操作
        return *this;
    }
    string* m_name;
};
int main()
{
    Person a;
    a.m_name = new string("张三");
    Person b;
    b.m_name = new string("李四");
    Person c;
    c.m_name = new string("王五");
    a = b = c;
    cout << *a.m_name << *b.m_name << *c.m_name << endl;
    return 0;
}

关系运算符重载

作用:重载关系运算符,可以让两个自定义类型对象进行对比操作

对比时需要注意指针:

  • 若不加*,则对比的是指针的地址
  • 若加*,对比的是指针指向的值
#include <iostream>
using namespace std;
class Person
{
public:
    Person(string name, int age)
    {
        m_name = new string(name);
        m_age = age;
    }
    //重载关系运算符 ==
    bool operator==(Person& a)
    {
        //m_name是指针,需要加解引用操作符,否则对比的是 指针指向的地址 而不是对比 对应的值
        if (*this->m_name == *a.m_name && this->m_age == a.m_age)
        {
            return true;
        }
        return false;
    }
    int m_age;
    string* m_name;
};
int main()
{
    Person a("张三", 10);
    Person b("张三", 10);
    cout << (a == b) << endl;
    return 0;
}

函数调用运算符重载

函数调用运算符()也可以重载

由于重载后使用的方法非常像函数的调用,因此成为仿函数

仿函数没有固定写法,非常灵活

允许匿名对象调用

class Person
{
public:
    int operator()(int a, int b)
    {
        return a + b;
    }
};
int main()
{
    Person a;
    cout << a(1, 2) << endl;
    //匿名对象调用
    cout << Person()(1, 2) << endl;
    return 0;
}
目录
相关文章
|
机器学习/深度学习 存储 算法
YOLO落地部署 | 一文全览YOLOv5最新的剪枝、量化的进展【必读】
YOLO落地部署 | 一文全览YOLOv5最新的剪枝、量化的进展【必读】
1905 0
|
Oracle 关系型数据库 数据安全/隐私保护
Oracle 11g客户端及PLSQL Developer配置|Instant Client Setup-64位|OraClientLite11g_x86
转载自:http://blog.csdn.net/xiaoyw71/article/details/45311589 Oracle 11g客户端 资源     下载资源,直接解压进行配置     Oracle官方资源:http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html  
4583 0
|
7天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1176 3
|
6天前
|
机器学习/深度学习 人工智能 前端开发
通义DeepResearch全面开源!同步分享可落地的高阶Agent构建方法论
通义研究团队开源发布通义 DeepResearch —— 首个在性能上可与 OpenAI DeepResearch 相媲美、并在多项权威基准测试中取得领先表现的全开源 Web Agent。
875 12
|
5天前
|
机器学习/深度学习 物联网
Wan2.2再次开源数字人:Animate-14B!一键实现电影角色替换和动作驱动
今天,通义万相的视频生成模型又又又开源了!Wan2.2系列模型家族新增数字人成员Wan2.2-Animate-14B。
456 10
|
16天前
|
人工智能 运维 安全
|
7天前
|
弹性计算 Kubernetes jenkins
如何在 ECS/EKS 集群中有效使用 Jenkins
本文探讨了如何将 Jenkins 与 AWS ECS 和 EKS 集群集成,以构建高效、灵活且具备自动扩缩容能力的 CI/CD 流水线,提升软件交付效率并优化资源成本。
331 0
|
7天前
|
消息中间件 Java Apache
SpringBoot集成RocketMq
RocketMQ 是一款开源的分布式消息中间件,采用纯 Java 编写,支持事务消息、顺序消息、批量消息、定时消息及消息回溯等功能。其优势包括去除对 ZooKeeper 的依赖、支持异步和同步刷盘、高吞吐量及消息过滤等特性。RocketMQ 具备高可用性和高可靠性,适用于大规模分布式系统,能有效保障消息传输的一致性和顺序性。
402 2