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

相关文章

Java对象转换的实现方式汇总

《Java对象转换的实现方式汇总》:本文主要介绍Java对象转换的多种实现方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java对象转换的多种实现方式1. 手动映射(Manual Mapping)2. Builder模式3. 工具类辅助映

利用Python调试串口的示例代码

《利用Python调试串口的示例代码》在嵌入式开发、物联网设备调试过程中,串口通信是最基础的调试手段本文将带你用Python+ttkbootstrap打造一款高颜值、多功能的串口调试助手,需要的可以了... 目录概述:为什么需要专业的串口调试工具项目架构设计1.1 技术栈选型1.2 关键类说明1.3 线程模

Python Transformers库(NLP处理库)案例代码讲解

《PythonTransformers库(NLP处理库)案例代码讲解》本文介绍transformers库的全面讲解,包含基础知识、高级用法、案例代码及学习路径,内容经过组织,适合不同阶段的学习者,对... 目录一、基础知识1. Transformers 库简介2. 安装与环境配置3. 快速上手示例二、核心模

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

Python中判断对象是否为空的方法

《Python中判断对象是否为空的方法》在Python开发中,判断对象是否为“空”是高频操作,但看似简单的需求却暗藏玄机,从None到空容器,从零值到自定义对象的“假值”状态,不同场景下的“空”需要精... 目录一、python中的“空”值体系二、精准判定方法对比三、常见误区解析四、进阶处理技巧五、性能优化

go 指针接收者和值接收者的区别小结

《go指针接收者和值接收者的区别小结》在Go语言中,值接收者和指针接收者是方法定义中的两种接收者类型,本文主要介绍了go指针接收者和值接收者的区别小结,文中通过示例代码介绍的非常详细,需要的朋友们下... 目录go 指针接收者和值接收者的区别易错点辨析go 指针接收者和值接收者的区别指针接收者和值接收者的