C++对象模型初探
2021/11/4 17:14:00
本文主要是介绍C++对象模型初探,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
- 成员变量和成员函数是分开存储的,意思就是说,成员函数严格来说不属于类的。C语言中的结构体是只存放变量,要在结构体之外声明定义实现函数。C++抽象出类的思想,把成员变量和成员函数封装在一个类中。C++在代码编写的时候看起来好像的确是把二者放在类内一起写,但是实际上成员函数跟成员变量是分开存储的。成员变量是属于类的,而成员函数则像静态成员函数一样是一块共享的数据,他们不属于类。因此当同一个类的不同对象访问同一个成员函数时,实际上是访问同一块地址空间。只有非静态成员变量才属于对象身上。
类内的数据和操作是分开存储的,并且每一个非内联成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共享一块代码。
- 空类的大小是1,因为类自己也可以作为一个被访问的“实例对象”(直接通过类名访问),因此就算是空类,内部也会维护一个char类型指针维护这个可以被当做实例访问的地址。这个大小为1的内存代表着这个类抽象出的实例的独有的地址(类似于准备出生的婴儿也要给他一个唯一的身份证),假如现在空类里面加入一个int型变量,则这个int型变量会找到这个大小为1的内存地址开始存储数据,所以会覆盖掉这个大小为1的内存。因此加入一个int变量之后,类的大小是4而不是5。
这里会引出内存对齐的概念,如果不修改对齐方式,则默认为内存对齐。内存对齐是什么意思呢?接着上面的例子,空类的大小为1,空位中加入了一个int型变量之后大小一共为4,那么此时在加入一个double变量之后,大小不是12,而是16。内存对齐就是把变量中内存最大的大小作为一个基准值,比如这里的double是8,基准值是8。空类中加入一个int之后,大小是4,对于8的基准值来说,一块基准值还剩下
(8- 4 )= 4
的大小,然而剩下的这4放不下一个double,因此需要去下一块基准值去存放这个double。加入了double之后,也就是16的大小了。这16的大小里面分为了两块基准值,第一块是int+4个空,第二块是存放double。以此类推……
- 前面说到对于一个类对象的成员函数是共用一块代码的,C++是通过提供特殊的对象指针(this指针)区分不同的对象。
this指针指向被调用的成员函数所属的对象。
#include <iostream> using namespace std; //创建一个人类 class Person { public: void func() {} //成员函数 string m_name; int age; }; int main () { Person p1; p1.func();//编译器回偷偷加入一个this指针 Person *this //实际上执行的是 void func(Person *this),this此时代表p1这个对象,以此来区分是哪个对象访问该成员函数 return 0; }
在上述代码中:当创建了一个p1对象之后,当p1调用成员函数func时,因为实际上func这个成员函数是只有一个,因此编译器会借助this指针偷偷传入p1对象,也就是 void func(Person *this),因为是p1调用的(p1.func();
),所以这个this是代表着p1这个对象。如果是其他对象调用func也是同理,虽然不同的对象都是共用一个func函数,但是依靠this指针可以让func区分是哪个对象在调用。
this指针是隐含在对象成员函数内的一种指针(构造拷贝析构函数中也有)。当一个对象被创建后,该对象的每一个成员函数都含有一个系统自动生成的隐含this指针,用以保存该对象的地址。也就是说虽然我们没有在成员函数的实现内写上this指针,但是编译器在编译时是会自动加上的。this指针称为“指向本对象的指针”,永远指向当前对象。
this指针是隐含在每一个类的非静态成员函数中的
,对象的非静态成员函数是不属于对象的,因此this指针当然也不属于对象,所以this指针不会影响sizeof(对象)
的结果。
this指针是C++实现封装的一种机制,它将对象和调用该对象的成员函数连接在一起。在外部看来,每一个对象都拥有自己的成员函数。一般情况下,并不用写this指针,系统会默认帮忙处理。
因为静态成员函数只有静态成员变量可以访问,而且静态成员函数是共享数据的,所以静态成员函数是没有this指针的。
- this指针指向对象本体,所以解引用之后就等于对象本体。
class Person { public: person(int age) { this->age = age; } int age; public: Person& plusAge(Person &p) { this->age += p.age; return *this; } }; int main() { Person p1(10); Person p2(10); p1.plusAge(p2).plusAge(p2);//链式编程思想,可以一直链接下去 //plusAge返回值是调用他的对象,所以p1.plusAge(p2)的返回值是加上p2年龄之后的p1对象,所以可以继续调用成员函数。 return 0; }
注意:plusAge成员函数的返回值是引用,表明返回的是对象本身。如果把引用去掉,就是值拷贝的返回,也就是说会返回一个跟对象本身的值一样的
另一个不存在的对象
(编译器自动做了浅拷贝)。如果把引用去掉之后,第一次调用会给p1成功添加年龄,但是由于第一次调用成功之后返回的已经不是p1对象本身了,所以后面的链式编程已经不会对p1有影响。
成员函数的返回值是引用还是非引用,看业务需要来定。
- 空指针访问成员函数
class Person { public: Person (int age) { this->m_age = age; } void show1() { cout << "该函数没用到this指针,所以空指针也可以访问"<< endl; } void show2(int age) { if (this == NULL) return;//用到了this指针,要做个判断 this->m_age = age;//相当于 NULL->m-age = age; 访问了不存在的地址。 cout << "该函数用到this指针,所以要做安全处理"<< endl; } int m_age; }; int main () { Person *p = NULL; p->show1(); //可以成功访问,show1里面没用到this指针 p->show2(10); //用到了this指针,如果不加安全判断,则程序会崩溃 return 0; }
①如果成员函数没有使用到this指针,则空指针可以方法该成员函数
②如果成员函数有使用到this指针,则空指针不能访问该成员函数(加上空判断防止该情况)
- 常函数和常对象
上面说的this指针,之所以this指针永远指向它的本体对象,
是因为this本质上是一个指针常量Person * const this
,指针的指向不能改变。
也就是说this指针绑定了本体对象,指向不能变,但是这个对象的变量可以变。
如果希望this指针指向的对象的变量也不要变,那么就要改成const Person * const this
既然this指针是编辑器自动会帮忙处理,那么我们就要在代码中给编译器一个提示,所以就引入了一个常函数的概念,就是在成员函数的后面加上const,那么当某个对象调用这个成员函数的时候,会自动为this指针加上const修饰,然后再传入成员函数。
**普通成员函数** class Person { public: void showAge() { //普通成员函数 this->m_age = 100;//该this指针是一个指针常量,永远指向p,但是p的成员属性可以改变。 } int m_age; }; int main() { Person p; p.showAge();//这里相当于是传入了 Person * const this 指针; //p.showAge(Person * const this); return 0; }
**常函数** class Person { public: void showAge() const { this->m_age = 100;//该操作错误 //加上了const之后,showAge这个成员函数就变成了常函数,在常函数内不能修改成员的属性 this->m_mutable_age = 100;//加了mutable关键字之后可以修改 } int m_age; mutable int m_mutable_age; }; int main() { Person p; p.showAge();//这里相当于是传入了 const Person * const this 指针; //p.showAge(const Person * const this); return 0; }
**常对象,常对象只能调用常函数,因为常对象的成员属性是不能改变的。 但是普通的成员函数是有可能会修改成员属性,而常函数是无法修改成员属性的。 所以一般常对象跟常函数是搭配使用的。 const Person p;//在类名前加const,变成了常对象 p.m_age = 100; //错误操作,常对象的成员属性不能改变 p.m_mutable_age = 100;//正确操作,常对象的成员属性不能改变,除非该成员属性有mutable修饰
这篇关于C++对象模型初探的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-27Rocket消息队列资料:新手入门指南
- 2024-11-27rocket消息队资料详解与入门指南
- 2024-11-27RocketMQ底层原理资料详解入门教程
- 2024-11-27RocketMQ项目开发资料:新手入门教程
- 2024-11-27RocketMQ项目开发资料详解
- 2024-11-27RocketMQ消息中间件资料入门教程
- 2024-11-27初学者指南:深入了解RocketMQ源码资料
- 2024-11-27Rocket消息队列学习入门指南
- 2024-11-26Rocket消息中间件教程:新手入门详解
- 2024-11-26RocketMQ项目开发教程:新手入门指南