• C++核心编程(三)



    前言

    C++核心编程(三)


    友元

    首先生活中我们自己家里面,有自己的卧室,也有客厅;客厅呢只要是个人来了就能坐,但是对于卧室来说就不是这样的,比如说自己的卧室属于私人的空间,不是随便一个人就可以访问的,但是对于你的好基友、好朋友只要给你打了招呼是可以访问的;对于一个类来说也是这样的,对于一个类的公共属性我们在哪里都能访问它,但是对于一个类的私有属性不是随都能访问的、如果某个程序想要访问的话、就必须和这个类打招呼告诉他我是你的好基友,这样我们就能正常访问其私有属性了;

    全局函数做友元

    class Building
    {
    private:
    	string bedroom;
    public:
    	string living_room;
    	Building()
    	{
    		bedroom = "卧室";
    		living_room = "客厅";
    	}
    
    };
    void ShowHouse1()
    {
    	Building p1;
    	cout << "void ShowHouse1()正在访问:" << p1.living_room << endl;
    
    }
    int main()
    {
    	ShowHouse1();
    	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

    首先我们知道我们的全局函数ShowHouse1是可以正常访问客厅的,应为living_room这个成员属性是公共的,是我们可以正常访问的,但是如果我们想要访问bedroom这个属性是访问不到的,因为这是个私有属性,编译器还会报错;
    在这里插入图片描述
    但是如果我们就是想要访问它怎么办?
    当然是成为Building类的好朋友啊!
    怎么成为它的好朋友?
    利用friend关键字加以修饰
    怎么修饰?
    当然是在需要成为好朋友的类里面声明一下就行了:
    在这里插入图片描述
    只需像这样声明,我们就能在ShowHouse1函数里面正常访问Building类的私有属性了;

    类做友元

    既然一个全局函数都能做类的友元,那么一个类能不能做另一个类的友元呢?比如说:A类、B类;
    我们能不能实现在A类中访问B类的私有属性呢?
    当然可以!!!
    我们只需向刚才一样声明一下就行了:

    class Building
    {
    private:
    	string bedroom;
    public:
    	string living_room;
    	Building()
    	{
    		bedroom = "卧室";
    		living_room = "客厅";
    	}
    
    };
    class Person
    {
    public:
    	Building* house;
    	Person()
    	{
    		house = new Building;
    	}
    	void SetHouse()
    	{
    		house->living_room = "客厅2号";
    		house->bedroom = "卧室2号";//正常情况下我们是无法访问的
    	}
    	void ShowHouse2()
    	{
    		cout << "void ShowHouse1()正在访问:" << house->living_room << endl;
    		cout << "void ShowHouse1()正在访问:" << house->bedroom << endl;//正常情况下我们是无法访问的
    	}
    };
    int main()
    {
    	Person p1;
    	p1.ShowHouse2();
    	p1.SetHouse();
    	p1.ShowHouse2();
    }
    
    • 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

    我们知道像这样一般情况下,我们是无法正常访问的;那我们就来声明一下呗!我们根据上面全局函数的声明来照猫画虎试一试:
    在这里插入图片描述
    这样声明编译器没报任何错误,我们来运行看看:
    在这里插入图片描述
    我们可以看到实际是符合预期的,证明我们这样声明是没有错的!

    成员函数做友元

    说到这个做友元,可把我折腾坏了,明明改了这个bug右出现了这个bug,可谓是屡屡碰壁!!人都要崩溃了!!😭😭😭;
    接下来我们来继续看看:(还是类做友元的代码)

    class Building
    {
    	friend class Person;//向BUilding类声明一下,Person类是你的好朋友
    private:
    	string bedroom;
    public:
    	string living_room;
    	Building()
    	{
    		bedroom = "卧室";
    		living_room = "客厅";
    	}
    
    };
    class Person
    {
    public:
    	Building* house;
    	Person()
    	{
    		house = new Building;
    	}
    	void SetHouse()
    	{
    		house->living_room = "客厅2号";
    		house->bedroom = "卧室2号";//正常情况下我们是无法访问的
    	}
    	void ShowHouse2()
    	{
    		cout << "void ShowHouse1()正在访问:" << house->living_room << endl;
    		cout << "void ShowHouse1()正在访问:" << house->bedroom << endl;//正常情况下我们是无法访问的
    	}
    };
    int main()
    {
    	Person p1;
    	p1.ShowHouse2();//输出客厅 卧室
    	p1.SetHouse();
    	p1.ShowHouse2();//输出客厅2 卧室2
    }
    
    • 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

    我们都知道,现在Person类是Building类的好朋友可以访问Building私有属性;可是现在BUilding类不干了,它说凭什么你Person类里面任何地方都能访问我?我岂不是太随便?我不同意!!我的对你访问我们地方做一些限制,我不能让你在类内任何地方都能访问我,我要求你只能通过ShowHouse1函数来访问我,其它任何地方访问我都不行!!!
    那我们应该怎么做?
    按照上面的方法照猫画虎呗:
    在这里插入图片描述
    我们来运行看看:
    发什么一大堆错误,头皮发麻🐵🐵🐵,我们来分析一下这些错误
    在这里插入图片描述
    我们先声明一下Person类:
    在这里插入图片描述
    现在声明好了,我们再来运行:
    还是一大堆Warning:
    在这里插入图片描述
    首先什么无法访问之类的东西,就是我们友元的声明是失败的;我们主要看看C2027错误:
    使用未定义类型?我不是声明了吗:
    在这里插入图片描述
    对,我们的确是声明了,编译器也的确知道了Person是个类,但是编译器仅仅知道Person是个类,编译器怎么知道这个类里面有些啥?你说Person类下有个ShowHouse2函数,编译器说“从上到这我也没发现Person里面有个啥啊!你说ShowHouse2函数是Person类下的就是啊?我反正没看见,我只知道Person是个类,但里面具体有啥我也不知道啊,难保你乱写的一个函数说是Person类里面的,为了防止出现Bug,就给你报错”;既然编译器不知道这里面有啥,我们就直接将Person类定义在Building前面:
    在这里插入图片描述
    我们运行看看:
    在这里插入图片描述
    它奶奶滴!又是一大堆错误:刚才说是Person都错误,现在又是BUilding的错误,我要崩溃了!!到底该怎么改?
    经过我多方查找,我终于找到了办法:
    首先我们把自己先想象成一个编译器,我们现在对这段代码就行检查:
    我们发现第一个错误出现在37行,我们去37行看看:
    我们发现在37我们new空间的时候,new出来的空间是Building类型,是会发生Building拷贝构造函数的调用的,但是编译器只知道Building是个类,里面有啥它也不是知道,他也不知道你实没实现这个函数,编译器也不敢给你乱提供;干脆就报错,让你来消除这个歧义;
    既然这样的话:我们干脆将声明和实现分开,先将两个类一起声明了在一起实现里面的成员函数:(先告诉编译器我有这个东西,实现在后面)
    在这里插入图片描述
    在这里插入图片描述

    我们可以看见现在现在只有58行有错误了,其实这个错误出现在我们的预期之内,因为我们只声明的Person类下面的ShowHouse2函数是Building类的好朋友,而SetHouse不是,但是却访问了私有属性。自然也就会报错,我们只需注释掉就行了:
    在这里插入图片描述
    我们发现完美解决!!!

    运算符重载

    运算符重载概念:对已有的运算符进行重新定义,赋予其另一种功能,以适应不同的数据类型;
    比如说class Person;
    +对于自定义类型来说是不能实现的把,+的操作数之内是编译器的自定义类型比如:int 、char、double等;
    如果我们想让+也能实现自定义类型的加法,我们可以利用函数来实现;C++很好的提供了这个技术;
    为了避免五花八门的函数名字,函数的名字编译器也已经帮我们想好了:
    返回值 operator+需要重载的运算符(参数);
    测试类:

    class Person
    {
    public:
    	int m_age;
    	int m_weight;
    	Person()
    	{
    		m_age = 0;
    		m_weight = 0;
    	}
    	 Person(int age, int weight) :m_age(age), m_weight(weight)
    	{
    		;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    加号运算符重载

    在这里插入图片描述
    正常情况下,我们发现肯定是出错的,我们线记住这个错误,在后面我们会经常见到;
    首先我们有两种实现该函数的方式:
    1、全局函数实现;
    2、成员函数实现;
    我们先来看看全局函数实现:
    在这里插入图片描述

    我们应该将返回值设置为Person类型,因为我们需要接收,同时也满足连+的实现;
    在这里插入图片描述
    我们再来看看成员函数实现:
    在这里插入图片描述
    在这里插入图片描述

    左移运算符重载

    Person p;
    我们想实现cout< 我们知道这是不行的,我们也可以通过operator重载一下:
    首先我们在成员函数内部无法实现这样的函数,我们实现出来的只能是:p< 在这里插入图片描述

    cout也是一个对象,属于ostream类;
    我们用全局函数实现一下:
    在这里插入图片描述

    在这里插入图片描述

    递增运算符重载

    前置++

    成员函数实现:
    在这里插入图片描述
    在这里插入图片描述

    后置++

    为了与前置++区分,我们用一个占位符来表示operator的参数:
    在这里插入图片描述

    在这里插入图片描述

    赋值运算符重载

    其实编译器给我们自动提供的有这个函数,我们如果细心一点的话,我们会发现:
    Person p1;Person p2;
    p1=p2;//是行的通的,但是我们并没有去实现它,我们还是能用;
    虽然编译器给我们提供了这个东西,但是也并是那么好用:

    class Person
    {
    public:
    	int age;
    	int* height;
    	Person()
    	{
    		age = 0;
    		height = new int;
    	}
    	~Person()
    	{
    		delete height;
    	}
    };
    int main()
    {
    	Person p1;
    	Person p2;
    	p1 = p2;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述
    我们可以发现p1和p2两个的height是不一样的,这个能理解,接下来我们在看看:
    在这里插入图片描述
    我们发现两个height的值都一样了,这是编译器提供的=出现的情况,从此我们应该已经开始发觉问题了,这似乎就是浅拷贝出现的情况,我们在销毁p1,p2的时候会对p1的height和p2的height进行释放,但是这两个height都指向同一块空间,我们会对其进行多次释放,显然编译器会崩,同时我们也少释放一块空间,同时会造成内存泄漏!!解决该问题就是利用深拷贝的方法,我们用自己写的重载=,不用编译器的;
    在这里插入图片描述

    在这里插入图片描述
    我们这样就很好的解决了这个问题;

    关系运算符重载

    在这里插入图片描述

    在这里插入图片描述

    函数调用运算符

    在这里插入图片描述
    在这里插入图片描述
    匿名对象的调用即用既毁;

  • 相关阅读:
    写给小白的ChatGPT和AI原理
    python 远程代码第一次推送
    [附源码]计算机毕业设计保护濒危动物公益网站Springboot程序
    mysql的trace追踪SQL工具,进行sql优化
    亚马逊云服务器成为了我的首选服务器
    UVA 10405【LCS】【背包】
    分段权重和变异反向学习的蝴蝶优化算法-附代码
    C++笔记
    移动硬盘格式化后数据还能恢复吗?
    全链路压测(11):聊聊稳定性预案
  • 原文地址:https://blog.csdn.net/qq_62106937/article/details/126541192