• C++核心编程:P8->类和对象----运算符重载


    本系列文章为黑马程序员C++教程学习笔记,前面的系列文章链接如下
    C++核心编程:P1->程序的内存模型
    C++核心编程:P2->引用
    C++核心编程:P3->函数提高
    C++核心编程:P4->类和对象----封装
    C++核心编程:P5->类和对象----对象的初始化和清理
    C++核心编程:P6->类和对象----C++对象模型和this指针
    C++核心编程:P7->类和对象----友元


    前言

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


    一、加号运算符重载

    对于内置的数据类型,编译器知道如何运算,比如下面我们都知道c的结果是20。

    int a = 10;
    int b = 10;
    int c = a +  b;
    
    • 1
    • 2
    • 3

    如果现在我们有个Person类,类中有两个成员变量m_A和m_B。现在我们有两个Person对象p1和p2,如果我们直接通过加号运算符+将两个对象的成员变量相加并创建出一个新的对象是不行的。

    class Person
    {
    public:
    	int m_A;
    	int m_B;
    };
    Person p1;
    p1.m_A = 10;
    p1.m_B = 10;
    Person p2;
    p2.m_A = 10;
    p2.m_B = 10;
    Person p3 = p1 + p2;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    1.1 成员函数重载+号运算符

    此时我们可以想个办法:通过自己写个成员函数,实现两个对象的成员变量相加并返回新的对象。

    class Person
    {
    public:
    	int m_A;
    	int m_B;
    
    	Person PersonAddPerson(Person& p)
    	{
    		Person tmp;
    		tmp.m_A = this->m_A + p.m_A;
    		tmp.m_B = this->m_B + p.m_B;
    		return tmp;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这种方法可行但也有缺点。如果多个人都实现了这样的函数,可能每个人的函数名都不一样,比如我写的叫PersonAddPerson,别人写的叫person_add_person…为了方便编写代码,编译器就直接给我们提供一个函数名:oprator+

    Person operator+(Person& p)
    {
    	Person tmp;
    	tmp.m_A = this->m_A + p.m_A;
    	tmp.m_B = this->m_B + p.m_B;
    	return tmp;
    }
    Person p3 = p1.operator+(p2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    当都使用编译器提供的这种名称时,就可以简化为两个数相加的形式,这就和我们预期的写法一致。以上就是通过成员函数重载+号

    Person p3 = p1 + p2;
    
    • 1

    整体实现代码如下

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Person {
    public:
    	Person() {};
    	Person(int a, int b)
    	{
    		this->m_A = a;
    		this->m_B = b;
    	}
    	//成员函数实现 + 号运算符重载
    	Person operator+(const Person& p) {
    		Person temp;
    		temp.m_A = this->m_A + p.m_A;
    		temp.m_B = this->m_B + p.m_B;
    		return temp;
    	}
    public:
    	int m_A;
    	int m_B;
    };
    
    void test01()
    {
    	Person p1;
    	p1.m_A = 10;
    	p1.m_B = 10;
    	Person p2;
    	p2.m_A = 10;
    	p2.m_B = 10;
    	//本质上是Person p3 = p1.operator+(p2)
    	Person p3 = p1 + p2;
    	cout << "mA:" << p3.m_A << " mB:" << p3.m_B << endl;
    }
    
    int main(void)
    {
    	test01();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    运行,可以看出可以通过+号直接完成两个对象的成员变量相加。
    在这里插入图片描述


    1.2 全局函数重载+号运算符

    我们也可以通过全局函数重载+号运算符,此时就需要两个参数。

    Person operator+(Person &p1, Person &p2)
    {
    	Person tmp;
    	tmp.m_A = p1.m_A + p2.m_A;
    	tmp.m_B = p1.m_B + p2.m_B;
    	return tmp;
    }
    Person p3 = operator+(p1, p2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    此时调用的方式可以简化为

    Person p3 = p1 + p2;
    
    • 1

    完整代码如下

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Person {
    public:
    	Person() {};
    	Person(int a, int b)
    	{
    		this->m_A = a;
    		this->m_B = b;
    	}
    public:
    	int m_A;
    	int m_B;
    };
    
    Person operator+(Person &p1, Person &p2)
    {
    	Person tmp;
    	tmp.m_A = p1.m_A + p2.m_A;
    	tmp.m_B = p1.m_B + p2.m_B;
    	return tmp;
    }
    
    void test01()
    {
    	Person p1;
    	p1.m_A = 10;
    	p1.m_B = 10;
    	Person p2;
    	p2.m_A = 10;
    	p2.m_B = 10;
    	//本质上是Person p3 = operator+(p1, p2)
    	Person p3 = p1 + p2;
    	cout << "mA:" << p3.m_A << " mB:" << p3.m_B << endl;
    }
    
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    运行,可以看出通过+直接完成两个对象的成员变量相加
    在这里插入图片描述


    1.3 运算符重载的函数重载

    如果现在我们想让Person变量和int类型变量相加,即将int类型的变量值加在Person的两个成员变量上,我们可以对运算符重载使用函数重载。

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Person {
    public:
    	Person() {};
    	Person(int a, int b)
    	{
    		this->m_A = a;
    		this->m_B = b;
    	}
    public:
    	int m_A;
    	int m_B;
    };
    
    Person operator+(const Person& p1, const Person& p2) {
    	Person temp(0, 0);
    	temp.m_A = p1.m_A + p2.m_A;
    	temp.m_B = p1.m_B + p2.m_B;
    	return temp;
    }
    
    //运算符重载 可以发生函数重载 
    Person operator+(const Person& p2, int val)
    {
    	Person temp;
    	temp.m_A = p2.m_A + val;
    	temp.m_B = p2.m_B + val;
    	return temp;
    }
    
    void test() {
    	Person p1(10, 10);
    
    	Person p2 = p1 + 10; //相当于 operator+(p1,10)
    	cout << "mA:" << p2.m_A << " mB:" << p2.m_B << endl;
    }
    
    int main() {
    	test();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    运行,可以发现调用的是下面那个Person operator+(const Person& p2, int val)
    在这里插入图片描述
    注:

    ①对于内置的数据类型的表达式的的运算符是不可能改变的。
    ②不要滥用运算符重载。


    二、左移运算符重载

    2.1 成员函数重载的缺陷

    我们可以通过cout + << 输出一些内置的数据类型。现在我们有个Person类,里面有m_A和m_B两个成员变量。如果想通过 << + Person对象就能直接输出m_A和m_B,则必须要重载才行。

    int a = 10;
    cout << a << endl; //可以输出内置数据类型
    Person p;
    p.m_A = 10;
    p.m_B = 10;
    cout << p << endl; //不行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    假如利用成员函数重载左移运算符,效果如下。可以看出调用方式p.operator<<(p)不是我们想要的效果,这里出现两个对象,而用 << 输出时只有1个对象

    class Person {
    public:
    	Person(int a, int b)
    	{
    		this->m_A = a;
    		this->m_B = b;
    	}
    	//调用方式就是p.operator<<(p)
    	void operator<<(Person& p){
    	}
    private:
    	int m_A;
    	int m_B;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    如果我们进行修改,由于成员函数重载的本质是对象去调用成员函数,这样又会使得cout在右侧

    class Person {
    public:
    	Person(int a, int b)
    	{
    		this->m_A = a;
    		this->m_B = b;
    	}
    	//调用方式就是p.operator<<(cout)
    	void operator<<(cout){
    	}
    private:
    	int m_A;
    	int m_B;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    因此我们通常不会使用成员函数重载<<运算符,因为无法实现cout在左侧,只能利用全局函数重载左移运算符。


    2.2 全局函数重载

    因此我们通过全局函数来重载左移运算符,大概框架如下

    class Person {
    public:
    	Person(int a, int b)
    	{
    		this->m_A = a;
    		this->m_B = b;
    	}
    private:
    	int m_A;
    	int m_B;
    };
    
    //全局函数实现左移重载
    //这样就能实现 cout << p
    void operator<<(cout, p) {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    我们首先看看cout的定义,可以看出cout的数据类型是ostream,而ostream就是标准输出流类。
    在这里插入图片描述
    于是我们将ostream拿过来,而cout这个对象只能有1个,于是我们传引用

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Person {
    public:
    	Person(int a, int b)
    	{
    		this->m_A = a;
    		this->m_B = b;
    	}
    public:
    	int m_A;
    	int m_B;
    };
    
    //全局函数实现左移重载
    //ostream对象只能有一个
    void operator<<(ostream& out, Person& p) {
    	out << "a:" << p.m_A << " b:" << p.m_B;
    }
    
    void test() {
    	Person p1(10, 20);
    	cout << p1;
    }
    
    int main() {
    	test();
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    运行,可以看出正确重载了<<并输出了p的成员变量。
    在这里插入图片描述


    2.3 一些问题

    可以看出最后没有换行。如果我们再加个 << endl,则会报错
    在这里插入图片描述
    这是因为我们在一行使用多个<<这种链式编程思想时。而我们这里返回的是void,即没有返回,所以无法追加<<。因此我们需要以引用的方式将返回cout。
    在这里插入图片描述
    通常我们在定义一个类是里面的一些成员变量权限为私有,所以可以让这个全局的重载函数作为类的友元。

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Person {
    	friend ostream& operator<<(ostream& out, Person& p);
    public:
    	Person(int a, int b)
    	{
    		this->m_A = a;
    		this->m_B = b;
    	}
    
    private:
    	int m_A;
    	int m_B;
    };
    
    //全局函数实现左移重载
    //ostream对象只能有一个
    ostream& operator<<(ostream& out, Person& p) {
    	out << "a:" << p.m_A << " b:" << p.m_B;
    	return out;
    }
    
    void test() {
    	Person p1(10, 20);
    	cout << p1 << "hello world" << endl; //链式编程
    }
    
    int main() {
    	test();
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    运行,可以看到重载函数也能直接访问私有成员变量。
    在这里插入图片描述


    三、递增运算符重载

    我们先来看看前置递增和后置递增的使用

    #include <iostream>
    using namespace std;
    
    int main()
    {
    	int a = 10;
    	cout << ++a << endl; //11
    	cout << a << endl;   //11
    
    	int b = 10; 
    	cout << b++ << endl; //10
    	cout << b << endl;   //11
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    可以看到前置递增是先将变量+1然后做其它操作。后置递增则是先做操作然后再+1。
    在这里插入图片描述
    现在我们想自己定义一个数据类型然后实现递增运算。比如这里我们自己定义一个数据类型MyInter,含有一个成员变量m_Num。默认构造函数给其一个初始值1,然后我们希望可以重载递增运算符实现递增操作。

    #include <iostream>
    using namespace std;
    
    class MyInter
    {
    public:
    	MyInter()
    	{
    		m_Num = 0;
    	}
    private:
    	int m_Num;
    };
    
    MyInter myint;
    cout << myint << endl; //0
    cout << ++myint << endl; //1
    cout << myint++ << endl; //1
    cout << myint << endl; //2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    如果要可以搭配使用cout,则需要先使用全局函数来重载左移运算符。

    #include <iostream>
    using namespace std;
    
    class MyInter
    {
    	friend ostream& operator<<(ostream &cout, MyInter myint);
    public:
    	MyInter()
    	{
    		m_Num = 0;
    	}
    private:
    	int m_Num;
    };
    
    ostream& operator<<(ostream &cout, MyInter myint)
    {
    	cout << myint.m_Num;
    	return cout;
    }
    
    int main()
    {
    	MyInter myint;
    	cout << myint << endl; //0
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    此时可以输出自己定义的数据类型了
    在这里插入图片描述


    3.1 重载前置

    首先我们实现前置递增

    #include <iostream>
    using namespace std;
    
    class MyInter
    {
    	friend ostream& operator<<(ostream &cout, MyInter myint);
    public:
    	MyInter()
    	{
    		m_Num = 0;
    	}
    	MyInter& operator++() //要返回引用
    	{
    		m_Num++; //先进行++运算
    		return *this;  //再将自身返回
    	}
    
    private:
    	int m_Num;
    };
    
    ostream& operator<<(ostream &cout, MyInter myint)
    {
    	cout << myint.m_Num;
    	return cout;
    }
    
    int main()
    {
    	MyInter myint;
    	cout << myint << endl; //0
    	cout << ++myint << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    运行,可以看到结果正确。
    在这里插入图片描述


    这里的重载函数如果返回值会出现一些问题。对于普通数据类型,我做连续两次递增操作,可以看到是对同一个变量连续做两次操作,结果是正确的

    int a = 0;
    cout << ++(++a) << endl;  //2
    cout << a << endl;  //2
    
    • 1
    • 2
    • 3

    如果是返回值,我们测试看一下

    #include <iostream>
    using namespace std;
    
    class MyInter
    {
    	friend ostream& operator<<(ostream &cout, MyInter myint);
    public:
    	MyInter()
    	{
    		m_Num = 0;
    	}
    	MyInter operator++()
    	{
    		m_Num++; //先进性++运算
    		return *this;  //再将自身返回
    	}
    
    private:
    	int m_Num;
    };
    
    ostream& operator<<(ostream &cout, MyInter myint)
    {
    	cout << myint.m_Num;
    	return cout;
    }
    
    int main()
    {
    	MyInter myint;
    	cout << ++(++myint) << endl; //2
    	cout << myint << endl; //1
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    可以看到返回值时连续做两次递增,只成功了一次,这是因为每一次返回的都是一个新的变量,下一次操作是对这个新的变量做操作。所以我们要返回引用,为了一直对一个数据做操作。
    在这里插入图片描述


    3.2 后置递增

    要实现后置递增,则需要对递增运算符重载实现函数重载。于是我们在参数里面加个int,代表一个站位参数,编译器就会认为这是后置递增。
    在这里插入图片描述
    后置递增对应的重载函数要返回值。如果返回引用就是返回一个局部对象的引用,局部对象会在函数结束后被释放。

    #include <iostream>
    using namespace std;
    
    class MyInter
    {
    	friend ostream& operator<<(ostream &cout, MyInter myint);
    public:
    	MyInter()
    	{
    		m_Num = 0;
    	}
    	MyInter& operator++()
    	{
    		m_Num++; //先进性++运算
    		return *this;  //再将自身返回
    	}
    	MyInter operator++(int)
    	{
    		//先记录当时结果
    		MyInter temp = *this;
    		//后递增
    		m_Num++;
    		//最后将记录结果做返回
    		return temp;
    	}
    private:
    	int m_Num;
    };
    
    ostream& operator<<(ostream &cout, MyInter myint)
    {
    	cout << myint.m_Num;
    	return cout;
    }
    
    int main()
    {
    	MyInter myint;
    	cout << myint++ << endl; //0
    	cout << myint << endl; //1
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    运行,结果正确
    在这里插入图片描述


    四、赋值运算符重载

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

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


    4.1 赋值运算符重载的一些问题

    我们先来测试下编译器自动提供的赋值运算符。我们创建一个Person类,包含一个成员变量m_Age,是个指向开辟在堆区数据age的指针。

    #include <iostream>
    using namespace std;
    
    class Person
    {
    public:
    	Person(int age)
    	{
    		//将年龄数据开辟到堆区
    		m_Age = new int(age);
    	}
    	//年龄的指针
    	int *m_Age;
    };
    
    void test01()
    {
    	Person p1(18);
    	Person p2(20);
    	p2 = p1;
    
    	cout << "p1的年龄为:" << *p1.m_Age << endl;
    	cout << "p2的年龄为:" << *p2.m_Age << endl;
    }
    
    int main() {
    	test01();
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    我们直接使用=,可以发现能够实现成员变量值得复制,没有错误。
    在这里插入图片描述
    由于类中有属性指向堆区,所以我们需要写个析构函数来释放这块内存

    #include <iostream>
    using namespace std;
    
    class Person
    {
    public:
    	Person(int age)
    	{
    		//将年龄数据开辟到堆区
    		m_Age = new int(age);
    	}
    	~Person()
    	{
    		if (m_Age != NULL)
    		{
    			delete m_Age;
    			m_Age = NULL;
    		}
    	}
    	//年龄的指针
    	int *m_Age;
    };
    
    void test01()
    {
    	Person p1(18);
    	Person p2(20);
    	p2 = p1;
    
    	cout << "p1的年龄为:" << *p1.m_Age << endl;
    	cout << "p2的年龄为:" << *p2.m_Age << endl;
    }
    
    int main() {
    	test01();
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    可以发现报错,这是由于出现了前面讲的深浅拷贝问题,即堆区数据重复释放。
    在这里插入图片描述


    4.2 解决方案

    所以我们重载=号时要进行深拷贝,而不是直接将值进行复制。

    #include <iostream>
    using namespace std;
    
    class Person
    {
    public:
    	Person(int age)
    	{
    		//将年龄数据开辟到堆区
    		m_Age = new int(age);
    	}
    	~Person()
    	{
    		if (m_Age != NULL)
    		{
    			delete m_Age;
    			m_Age = NULL;
    		}
    	}
    	void operator=(Person &p)
    	{
    		if (m_Age != NULL) //如果自己开辟的内存释放掉
    		{
    			delete m_Age;
    			m_Age = NULL;
    		}
    		m_Age = new int(*p.m_Age); //重新开辟一块内存
    	}
    	//年龄的指针
    	int *m_Age;
    };
    
    void test01()
    {
    	Person p1(18);
    	Person p2(20);
    	p2 = p1;
    
    	cout << "p1的年龄为:" << *p1.m_Age << endl;
    	cout << "p2的年龄为:" << *p2.m_Age << endl;
    }
    
    int main() {
    	test01();
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    运行,可以看出不会出现浅拷贝了
    在这里插入图片描述
    但是还没完。对于普通类型,如果有这种连等,则是把最右边那个数赋值给左边的所有数。

    int a = 10;
    int b = 20;
    int c = 30;
    c = b = a;
    cout << "a = " << a << endl;  //10
    cout << "b = " << b << endl;  //10
    cout << "c = " << c << endl;  //10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    所以需要对重载函数进行修改。注意,不要返回值,否则又会调用拷贝构造函数创建一个副本

    #include <iostream>
    using namespace std;
    
    class Person
    {
    public:
    	Person(int age)
    	{
    		//将年龄数据开辟到堆区
    		m_Age = new int(age);
    	}
    	~Person()
    	{
    		if (m_Age != NULL)
    		{
    			delete m_Age;
    			m_Age = NULL;
    		}
    	}
    	Person& operator=(Person &p)
    	{
    		if (m_Age != NULL) //如果自己开辟的内存释放掉
    		{
    			delete m_Age;
    			m_Age = NULL;
    		}
    		m_Age = new int(*p.m_Age); //重新开辟一块内存
    
    		return *this;
    	}
    	//年龄的指针
    	int *m_Age;
    };
    
    void test01()
    {
    	Person p1(18);
    	Person p2(20);
    	Person p3(30);
    	p3 = p2 = p1;
    
    	cout << "p1的年龄为:" << *p1.m_Age << endl;
    	cout << "p2的年龄为:" << *p2.m_Age << endl;
    	cout << "p3的年龄为:" << *p3.m_Age << endl;
    }
    
    int main() {
    	test01();
    	system("pause");
    	return 0;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    运行,可以看出成功实现了连等。
    在这里插入图片描述


    五、关系运算符重载

    关系运算符包含==和!=。如果现在我们想对比两个自定义的数据类型,则需要重载关系运算符。假如现在我有个Person类,包含一个string类型的成员变量name和int类型的成员变量age。如果两个Person对象的name和age相等就打印相等,否则打印不相等。

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Person
    {
    public:
    	bool operator==(Person &p)
    	{
    		if (this->age == p.age && this->name == p.name)
    			return true;
    		else
    			return false;
    
    
    	}
    	bool operator!=(Person &p)
    	{
    		if (this->age != p.age || this->name != p.name)
    			return true;
    		else
    			return false;	
    	}
    public:
    	Person(string m_Name, int m_Age)
    	{
    		name = m_Name;
    		age = m_Age;
    	}
    public:
    	string name;
    	int age;
    };
    
    int main()
    {
    	Person p1("Tom", 18);
    	Person p2("Tom", 18);
    	if (p1 == p2)
    	{
    		cout << "p1 和 p2 是相等的" << endl;
    	}
    	if (p1 != p2)
    	{
    		cout << "p1 和 p2 是不相等的" << endl;
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    运行,可以看到结果正确。
    在这里插入图片描述


    六、函数调用运算符重载

    • 函数调用运算符 () 也可以重载
    • 由于重载后使用的方式非常像函数的调用,因此称为仿函数
    • 仿函数没有固定写法,非常灵活

    例: 假设现在我们要创建一个MyPrint类,通过重载函数调用运算符完成字符串打印输出。

    #include <iostream>
    #include <string>
    using namespace std;
    
    class MyPrint
    {
    public:
    	void operator()(string str)
    	{
    		cout << str << endl;
    	}
    };
    
    int main()
    {
    	MyPrint myfunc;
    	myfunc("hello world");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行,可以看出类似于函数一样完成了字符串的打印输出。
    在这里插入图片描述


    例: 假设现在要创建一个MyAdd类,通过重载函数调用运算符完成两个整数相加。

    #include <iostream>
    #include <string>
    using namespace std;
    
    class MyAdd
    {
    public:
    	int operator()(int a, int b)
    	{
    		return a + b;
    	}
    };
    
    int main()
    {
    	MyAdd func2;
    	cout << func2(10, 20) << endl;
    	cout << MyAdd()(10, 20) << endl; //匿名对象调用  
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    运行,可以看到结果正确。其中,我们这里使用了匿名函数调用,即先通过MyAdd()创建一个匿名对象,这个匿名对象在当前行执行结束后会被释放,然后为这个匿名对象调用了重载的()运算符函数。
    在这里插入图片描述

  • 相关阅读:
    计算机网络(自顶向下方法)-传输层
    监控直流防雷浪涌保护器综合方案
    linux- socket编程-直接获取网卡-packet- sokcket
    需求一款呼叫通信软件,也可定制开发可谈价
    SQL常见题型总结
    面向6G的编码调制和波形技术
    CSDN21天学习挑战赛 - 第二篇打卡文章
    【Mysql专题】使用Mysql做排行榜,线上实例
    Matlab模式分类代码和手册
    C++之可变参数模板
  • 原文地址:https://blog.csdn.net/InnerPeaceHQ/article/details/124841747