文章目录
前言
C++核心编程(三)
友元
首先生活中我们自己家里面,有自己的卧室,也有客厅;客厅呢只要是个人来了就能坐,但是对于卧室来说就不是这样的,比如说自己的卧室属于私人的空间,不是随便一个人就可以访问的,但是对于你的好基友、好朋友只要给你打了招呼是可以访问的;对于一个类来说也是这样的,对于一个类的公共属性我们在哪里都能访问它,但是对于一个类的私有属性不是随都能访问的、如果某个程序想要访问的话、就必须和这个类打招呼告诉他我是你的好基友,这样我们就能正常访问其私有属性了;
全局函数做友元
classBuilding{private:
string bedroom;public:
string living_room;Building(){
bedroom ="卧室";
living_room ="客厅";}};voidShowHouse1(){
Building p1;
cout <<"void ShowHouse1()正在访问:"<< p1.living_room << endl;}intmain(){ShowHouse1();return0;}
首先我们知道我们的全局函数ShowHouse1是可以正常访问客厅的,应为living_room这个成员属性是公共的,是我们可以正常访问的,但是如果我们想要访问bedroom这个属性是访问不到的,因为这是个私有属性,编译器还会报错;
但是如果我们就是想要访问它怎么办?
当然是成为Building类的好朋友啊!
怎么成为它的好朋友?
利用friend关键字加以修饰
怎么修饰?
当然是在需要成为好朋友的类里面声明一下就行了:
只需像这样声明,我们就能在ShowHouse1函数里面正常访问Building类的私有属性了;
类做友元
既然一个全局函数都能做类的友元,那么一个类能不能做另一个类的友元呢?比如说:A类、B类;
我们能不能实现在A类中访问B类的私有属性呢?
当然可以!!!
我们只需向刚才一样声明一下就行了:
classBuilding{private:
string bedroom;public:
string living_room;Building(){
bedroom ="卧室";
living_room ="客厅";}};classPerson{public:
Building* house;Person(){
house =new Building;}voidSetHouse(){
house->living_room ="客厅2号";
house->bedroom ="卧室2号";//正常情况下我们是无法访问的}voidShowHouse2(){
cout <<"void ShowHouse1()正在访问:"<< house->living_room << endl;
cout <<"void ShowHouse1()正在访问:"<< house->bedroom << endl;//正常情况下我们是无法访问的}};intmain(){
Person p1;
p1.ShowHouse2();
p1.SetHouse();
p1.ShowHouse2();}
我们知道像这样一般情况下,我们是无法正常访问的;那我们就来声明一下呗!我们根据上面全局函数的声明来照猫画虎试一试:
这样声明编译器没报任何错误,我们来运行看看:
我们可以看到实际是符合预期的,证明我们这样声明是没有错的!
成员函数做友元
说到这个做友元,可把我折腾坏了,明明改了这个bug右出现了这个bug,可谓是屡屡碰壁!!人都要崩溃了!!😭😭😭;
接下来我们来继续看看:(还是类做友元的代码)
classBuilding{friendclassPerson;//向BUilding类声明一下,Person类是你的好朋友private:
string bedroom;public:
string living_room;Building(){
bedroom ="卧室";
living_room ="客厅";}};classPerson{public:
Building* house;Person(){
house =new Building;}voidSetHouse(){
house->living_room ="客厅2号";
house->bedroom ="卧室2号";//正常情况下我们是无法访问的}voidShowHouse2(){
cout <<"void ShowHouse1()正在访问:"<< house->living_room << endl;
cout <<"void ShowHouse1()正在访问:"<< house->bedroom << endl;//正常情况下我们是无法访问的}};intmain(){
Person p1;
p1.ShowHouse2();//输出客厅 卧室
p1.SetHouse();
p1.ShowHouse2();//输出客厅2 卧室2}
我们都知道,现在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+需要重载的运算符(参数);
测试类:
classPerson{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、成员函数实现;
我们先来看看全局函数实现:
我们应该将返回值设置为Person类型,因为我们需要接收,同时也满足连+的实现;
我们再来看看成员函数实现:
左移运算符重载
Person p;
我们想实现cout<<p<<endl;
我们知道这是不行的,我们也可以通过operator重载一下:
首先我们在成员函数内部无法实现这样的函数,我们实现出来的只能是:p<<cout这种形式,也我们预期不符合:
cout也是一个对象,属于ostream类;
我们用全局函数实现一下:
递增运算符重载
前置++
成员函数实现:
后置++
为了与前置++区分,我们用一个占位符来表示operator的参数:
赋值运算符重载
其实编译器给我们自动提供的有这个函数,我们如果细心一点的话,我们会发现:
Person p1;Person p2;
p1=p2;//是行的通的,但是我们并没有去实现它,我们还是能用;
虽然编译器给我们提供了这个东西,但是也并是那么好用:
classPerson{public:int age;int* height;Person(){
age =0;
height =newint;}~Person(){delete height;}};intmain(){
Person p1;
Person p2;
p1 = p2;return0;}
我们可以发现p1和p2两个的height是不一样的,这个能理解,接下来我们在看看:
我们发现两个height的值都一样了,这是编译器提供的=出现的情况,从此我们应该已经开始发觉问题了,这似乎就是浅拷贝出现的情况,我们在销毁p1,p2的时候会对p1的height和p2的height进行释放,但是这两个height都指向同一块空间,我们会对其进行多次释放,显然编译器会崩,同时我们也少释放一块空间,同时会造成内存泄漏!!解决该问题就是利用深拷贝的方法,我们用自己写的重载=,不用编译器的;
我们这样就很好的解决了这个问题;
关系运算符重载
函数调用运算符
匿名对象的调用即用既毁;
版权归原作者 南猿北者 所有, 如有侵权,请联系我们删除。