2021-11-08 C++封装继承多态---C++对象模型和this指针、友元、运算符重载(附代码理解)

本文主要是介绍2021-11-08 C++封装继承多态---C++对象模型和this指针、友元、运算符重载(附代码理解),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

3. C++对象模型和this指针

3.1 成员变量和成员函数分开存储

在C++中,类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上。

#include<iostream>
using namespace std;
class person
{int m_a; //非静态成员变量  属于类的对象上的static int m_b;//静态成员变量    不属于类的对象上的void func() {} //非静态成员变量    不属于类的对象上的static void func2() {} 
};
int person::m_b = 10;
void test01()
{person p;//空对象占用的内存空间为:???  1个字节//c++ 编译器会给每个空对象也分配一个字节空间,为了区分对象占内存的位置//每个空对象也应该有一个独一无二的内存cout << "size of p=="<<sizeof(p) << endl;
}
void test02()
{person p;cout << "size of p==" << sizeof(p) << endl;
}
int main() {test02();system("pause");return 0;
}

3.2 this指针概念

我们知道在C++中成员变量和成员函数是分开存储的
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码那么问题是:这—块代码是如何区分那个对象调用自己的呢?
C++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的—种指针
this指针不需要定义,直接使用即可
this指针的用途:·当形参和成员变量同名时,可用this指针来区分 ·在类的非静态成员函数中返回对象本身,可使用return *this

#include<iostream>
using namespace std;
//1 解决名称冲突//2 返回对象本身用 return *this
class person
{
public:person(int age){//this 指针指向的是 被调用的成员函数 所属于的对象this->age = age;}person &personaddage(person& p){this->age += age;//this 指向p2的指针, *this 指向的就是p2的对象的本体return *this;}int age;
};
//1 解决名称冲突
void test01()
{person p1(18);cout << "p1的年龄" <<p1.age<< endl;
}
//2 返回对象本身用 return *this
void test02()
{person p1(18);person p2(18);//链式编程思想  &personaddage 为了套猴p2.personaddage(p1).personaddage(p1).personaddage(p1);cout << "p2的年龄" << p2.age << endl;
}
int main() {//test01();test02();system("pause");return 0;
}

3.3 空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性

#include<iostream>
using namespace std;
//
class person
{
public:void showclassname(){cout << "this is person class" << endl;}void showpersonage(){//报错原因是因为传入的指针为空指针if (this == NULL)  //加上这个就可以了{return;}cout<<"age=" <<m_age<< endl;}int m_age;
};
void test01()
{person* p =NULL;p->showclassname();p->showpersonage();
}int main() {test01();system("pause");return 0;
}

3.4 const修饰成员函数

常函数常对象
成员函数后加const后我们称为这个函数为常函数声明对象前加const称该对象为常对象
常函数内不可以修改成员属性常对象只能调用常函数
成员属性声明时加关键字mutable后,在常函数中依然可以修改
#include<iostream>
using namespace std;
//
class person
{
public://常函数// this 指针本质为指针常量,指向不可以修改// const person * const this ; == this指针// 在成员函数后面加上const之后,修饰的是this的指向,让指针指向的值也不可以修改void showperson() const{//this 指针是  隐含在每一个非静态成员函数中的 一种指针//this->m_a = 100;//this = NULL;  //this 指针是不可以修改指针的指向的this->m_b = 100;}int m_a;mutable int m_b; //特殊变量,即使在常函数中,也可以修改这个值。加上关键字mutable
};void test01()
{person p;p.showperson();cout << "good baby" << endl;
}
//常对象
void test02()
{const person p2; //在对象前加const,变成了常对象//p2.m_a = 100;p2.m_b = 100; // m_b是特殊值,在常对象里面也可以修改的//常对象只可以调用常函数
}
int main() {test01();system("pause");return 0;
}

4.友元

生活中你的家有客厅(Public),有你的卧室(Private)
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去但是呢,你也可以允许你的好闺蜜好基友进去。
在程序里,有些私有属性也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术
友元的目的就是让一个函数或者类访问另—个类中私有成员
友元的关键字为friend
友元的三种实现: .全局函数做友元 ·类做友元 ·成员函数做友元

4.1全局函数做友元

#include<iostream>
using namespace std;
//建筑物的类
class building
{//goodgay 是building好朋友  可以访问私有成员friend void goodgay(building* building);
public:building(){m_sittingroom = "客厅";m_bedroom = "卧室";}
public:string m_sittingroom; //客厅
private:string m_bedroom; //卧室
};
//全局函数
void goodgay(building *b)
{cout <<"好基友全局函数 正在访问" <<b->m_sittingroom <<endl;cout << "好基友全局函数 正在访问" << b->m_bedroom << endl;  //私有怎么访问
}void test01()
{building b2;goodgay(&b2);
}
int main() {test01();system("pause");return 0;
}

4.2类做友元

#include<iostream>
using namespace std;
//建筑物的类
class Building;
class goodgay
{
public:goodgay();void visit(); //参观函数 访问building中的属性Building * building;
private:
};class Building
{friend class goodgay;  // goodgay类是本类的好朋友,可以访问私有成员
public:Building();  //初始化
public:string m_sittingroom;
private:string m_bedroom;
};//类外写成员函数
Building::Building()
{m_sittingroom = "客厅";m_bedroom = "卧室";
}
goodgay::goodgay()
{//创建建筑物的对象building = new Building;
}void goodgay::visit()
{cout<<"好基友正在访问:" <<building->m_sittingroom << endl;cout << "好基友正在访问:" << building->m_bedroom << endl;
}
void test01()
{goodgay gg;gg.visit();
}
int main() {test01();system("pause");return 0;
}

4.3成员函数做友元

#include<iostream>
using namespace std;
//
class Building;
class goodgay
{
public:goodgay();void visit(); //参观函数 访问building中的私有的成员void visit2(); //参观函数 不访问building中的私有的成员Building* building;
};
class Building
{//告诉编译器 goodgay类下的visit成员函数作为本类的好朋友,可以访问私有成员friend void goodgay::visit();
public:Building();
public:string m_sittingroom;
private:string m_bedroom;
};//类外写成员函数
Building::Building()
{m_sittingroom = "客厅";m_bedroom = "卧室";
}
goodgay::goodgay()
{//创建建筑物的对象building = new Building;
}void goodgay::visit()
{cout << "visit函数正在访问:" << building->m_sittingroom << endl;cout << "visit函数正在访问:" << building->m_bedroom << endl;
}
void goodgay::visit2()
{cout << "visit2函数正在访问:" << building->m_sittingroom << endl;//cout << "visit2函数正在访问:" << building->m_bedroom << endl;
}
void test01()
{goodgay gg;gg.visit();gg.visit2();
}
int main() {test01();system("pause");return 0;
}

5运算符重载

5.1加号运算符重载

三种重载方法!

#include<iostream>
using namespace std;class person
{
public://1 成员函数重载+号person operator+(person & p){person temp;temp.m_a = this->m_a + p.m_a;temp.m_b = this->m_b + p.m_b;return temp;}int m_a;int m_b;
};
//2 全局函数重载+号
person operator+(person& p1, person& p2)
{person temp;temp.m_a = p1.m_a + p2.m_a;temp.m_b = p1.m_b + p2.m_b;return temp;
}
//函数重载的版本
person operator+(person& p1, int num)
{person temp;temp.m_a = p1.m_a + num;temp.m_b = p1.m_b + num;return temp;
}
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;//全局函数重载本质person p3 = operator+(p1, p2);//person p3 = p1 + p2;//运算符重载,也可以发生函数重载person p4 = p1 + 10; //person + intcout <<"p3.m_a" << p3.m_a  << endl;cout <<"p3.m_b" << p3.m_b << endl;cout << "p4.m_a" << p3.m_a << endl;cout << "p4.m_b" << p3.m_b << endl;
}int main() {test01();system("pause");return 0;
}

5.2左移运算符重载

输出自定义数据类型

#include<iostream>
using namespace std;class person
{friend ostream& operator<<(ostream& cout, person p);friend void test01();
public:// 1 处成员函数重载  左移运算符  p.operator<<(cout)  简化版本 p<<cout//不会利用成员函数重载<<运算符,因为无法实现 cout在左侧//void operator<<(person& p)//{//}
private:int m_a;int m_b;
};
//只能利用全局函数重载左移运算符
ostream & operator<<(ostream& cout, person p)   //本质 operator<<(cout,p) 简化 cout << p
{cout << "m_a = " << p.m_a << "m_b = " << p.m_b;return cout;
}void test01()
{person p;p.m_a = 10;p.m_b = 10;//链式编程思想cout << p<<endl;
}int main() {test01();system("pause");return 0;
}

5.3递增运算符重载

#include<iostream>
using namespace std;
//重载++运算符
//前置和后置++运算符
class myinterger
{friend ostream& operator<<(ostream& cout, myinterger myint);  //好朋友友元
public:myinterger(){m_num = 0;}//重载前置++运算符  返回引用是为了一直对一个数据进行操作myinterger & operator++(){//先进行++的运算m_num++;//再将自身返回return *this;}//重载后置++运算符myinterger operator++(int)  //int让编译器知道你用的是后置运算符重载。//int 代表占位参数,可以用来区分前置和后置的递增{//先 记录当时的结果myinterger temp = *this;//后 递增m_num++;//最后将记录的结果进行返回的操作return temp;}
private:int m_num;
};
//重载左移运算符
ostream& operator<<(ostream& cout, myinterger myint)   //本质 operator<<(cout,p) 简化 cout << p
{cout << myint.m_num;return cout;
}
void test01()
{myinterger myint;cout<<myint <<endl;
}//重载后置++运算符
void test02()
{myinterger myint;cout << myint++ << endl;cout << myint << endl;
}
//前置递增返回引用,后置递增返回值
int main() {test02();system("pause");return 0;
}

5.4赋值运算符重载

#include<iostream>
using namespace std;
//
class person
{
public:person(int age){m_age = new int(age);cout << "取" << endl;cout << this->m_age << endl;}~person(){if (m_age != NULL){delete m_age;m_age = NULL;cout <<"析构" << endl;}}//重载赋值运算符person & operator=(person& p){//编译器提供浅拷贝//m_age = p.m_age;//应该先判断是否有属性在堆区,先释放干净在执行深拷贝cout << this->m_age << endl;if (m_age != NULL)      //将p2的的堆区数据清空,然后放入p1的数据!!!!!!!!!!!!!!!{delete m_age;m_age = NULL;cout << "释放" << endl;cout << this->m_age << endl;}//深拷贝m_age = new int(*p.m_age);//返回对象本体cout << this->m_age << endl;return *this;}int* m_age;
};void test01()
{person p1(19);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;
}

5.5关系运算符重载

#include<iostream>
using namespace std;
//
class person
{
public:person(string name, int age){m_name = name;m_age = age;}//重载 == 号bool operator==(person& p){if (this->m_name == p.m_name && this->m_age == p.m_age){return true;}return false;}//重载 != 号bool operator!=(person& p){if (this->m_name == p.m_name && this->m_age == p.m_age){return false;}return true;}string m_name;int m_age;
};
void test01()
{person p1("tom", 18);person p2("jack", 22);if (p1 == p2){cout << "p1 和p2 是相等的" << endl;}else{cout <<"布线等" << endl;}if (p1 != p2){cout << "p1 和p2 是相等的" << endl;}else{cout << "布线等" << endl;}
}
int main() {test01();system("pause");return 0;
}

5.6函数调用运算符重载

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

#include<iostream>
using namespace std;
//打印输出类
class myprint
{
public://重载函数调用运算符void operator()(string test){cout << test<< endl;}
};void myprint02(string test) {cout <<test  <<endl;
}void test01()
{myprint myprint;myprint("hello world"); //由于使用起来非常类似于函数调用,因此称为仿函数myprint02("hello world");
}//仿函数非常灵活,没有固定的写法  test01, test02
//加法类
class Myadd
{
public:int operator()(int num1, int num2){return num1, num2;}
};
void test02()
{Myadd myadd;int ret = myadd(100, 100);cout <<"ret = " << ret<< endl;//匿名函数对象   运行玩一次就会被释放掉cout << Myadd()(100, 100) << endl;
}
int main() {test02();system("pause");return 0;
}

这篇关于2021-11-08 C++封装继承多态---C++对象模型和this指针、友元、运算符重载(附代码理解)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1048354

相关文章

Golang的CSP模型简介(最新推荐)

《Golang的CSP模型简介(最新推荐)》Golang采用了CSP(CommunicatingSequentialProcesses,通信顺序进程)并发模型,通过goroutine和channe... 目录前言一、介绍1. 什么是 CSP 模型2. Goroutine3. Channel4. Channe

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一

在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码

《在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码》在MyBatis的XML映射文件中,trim元素用于动态添加SQL语句的一部分,处理前缀、后缀及多余的逗号或连接符,示... 在MyBATis的XML映射文件中,<trim>元素用于动态地添加SQL语句的一部分,例如SET或W

Spring常见错误之Web嵌套对象校验失效解决办法

《Spring常见错误之Web嵌套对象校验失效解决办法》:本文主要介绍Spring常见错误之Web嵌套对象校验失效解决的相关资料,通过在Phone对象上添加@Valid注解,问题得以解决,需要的朋... 目录问题复现案例解析问题修正总结  问题复现当开发一个学籍管理系统时,我们会提供了一个 API 接口去

一文带你理解Python中import机制与importlib的妙用

《一文带你理解Python中import机制与importlib的妙用》在Python编程的世界里,import语句是开发者最常用的工具之一,它就像一把钥匙,打开了通往各种功能和库的大门,下面就跟随小... 目录一、python import机制概述1.1 import语句的基本用法1.2 模块缓存机制1.

使用C#代码计算数学表达式实例

《使用C#代码计算数学表达式实例》这段文字主要讲述了如何使用C#语言来计算数学表达式,该程序通过使用Dictionary保存变量,定义了运算符优先级,并实现了EvaluateExpression方法来... 目录C#代码计算数学表达式该方法很长,因此我将分段描述下面的代码片段显示了下一步以下代码显示该方法如

深入理解C语言的void*

《深入理解C语言的void*》本文主要介绍了C语言的void*,包括它的任意性、编译器对void*的类型检查以及需要显式类型转换的规则,具有一定的参考价值,感兴趣的可以了解一下... 目录一、void* 的类型任意性二、编译器对 void* 的类型检查三、需要显式类型转换占用的字节四、总结一、void* 的

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取

C++中实现调试日志输出

《C++中实现调试日志输出》在C++编程中,调试日志对于定位问题和优化代码至关重要,本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳,希望对大家有所帮助... 目录1. 使用 #ifdef _DEBUG 宏2. 加入时间戳:精确到毫秒3.Windows 和 MFC 中的调试日志方法MFC

深入理解Redis大key的危害及解决方案

《深入理解Redis大key的危害及解决方案》本文主要介绍了深入理解Redis大key的危害及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、背景二、什么是大key三、大key评价标准四、大key 产生的原因与场景五、大key影响与危