4.3 C++对象模型和this指针

4.3 C++对象模型和this指针

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

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

#include <iostream>class Person {
public:Person() {mA = 0;} //非静态成员变量占对象空间int mA;//静态成员变量不占对象空间static int mB;//函数也不占对象空间,所有函数共享一个函数实例void func() {std::cout << "mA:" << this->mA << std::endl;} //静态成员函数也不占对象空间static void sfunc() {}
};int main() {//实例化一个对象为p1Person p1;std::cout << "Person:"<<sizeof(Person) << std::endl;std::cout << "p1:" << sizeof(p1) << std::endl;return 0;
}

运行结果如下:

image-20231220115319320

4.3.2 this指针概念

通过4.3.1我们知道在C++中成员变量和成员函数是分开存储的
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
那么问题是:这一块代码是如何区分那个对象调用自己的呢?
c++通过提供特殊的对象指针,this指针,解决上述问题。

this指针指向被调用的成员函数所属的对象

this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可

this指针是一个常量指针,可以看做const type * this ,指针的指向不能修改,this = NULL这样是错的。

this指针的用途:

当形参和成员变量同名时,可用this指针来区分

在类的非静态成员函数中返回对象本身,可使用return *this

#include <iostream>class Person {
public:Person(int age) {//1 使用this区分形参和成员变量this->age = age;} Person& PersonAddPerson(Person p) {this->age += p.age;//2 返回对象本身,this为指针,所以加上*return *this;} //定义一个成员变量,属性为publicint age;
};void test01()
{   //实例化一个对象为p1,会调用有参构造函数Person p1(10);//打印p1.age的结果std::cout << "p1.age:" << p1.age << std::endl;//实例化一个对象为p2Person p2(12);//p2.PersonAddPerson(p1)这是使用p2对象调用成员函数PersonAddPerson(p1),//成员函数返回值是对象p2,然后再次调用成员函数p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);std::cout << "p2.age:" << p2.age << std::endl;}int main() {test01();return 0;
}

运行结果如下:

image-20231220122148474

#include <iostream>class MyClass {
public:void printAddress() {std::cout << "Address of the current object: " << this << std::endl;}void modifyObject() {// 下面的语句是非法的,会导致编译错误// this = nullptr;  // Error: assignment of read-only parameter 'this'// 修改成员变量是合法的data = 42;}private:int data;
};int main() {MyClass obj;// 调用成员函数,显示对象地址obj.printAddress();// 调用修改对象的函数obj.modifyObject();return 0;
}
4.3.3 空指针访问成员函数

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

示例:

#include <iostream>class Person {
public:void ShowClassName() {std::cout << "Person类" << std::endl;} void ShowPerson() {if(this == NULL)return ;std::cout << "age:" << age << std::endl;} int age;};void test01()
{   //创建一个对象指针Person* p1 = NULL;//使用对象指针调用成员函数p1->ShowClassName();//使用对象指针调用成员函数,成员函数使用了this指针,空对象指针就使用不了   p1->ShowPerson();}int main() {test01();return 0;
}

执行结果如下:

image-20231220131818347

成员函数去掉this判断部分

#include <iostream>class Person {
public:void ShowClassName() {std::cout << "Person类" << std::endl;} void ShowPerson() {/*if(this == NULL)return ;*/std::cout << "age:" << age << std::endl;} int age;};void test01()
{   //创建一个对象指针Person* p1 = NULL;//使用对象指针调用成员函数p1->ShowClassName();//使用对象指针调用成员函数,成员函数使用了this指针,空对象指针就使用不了p1->ShowPerson();}int main() {test01();return 0;
}

再次编译执行结果如下

image-20231220132755137

对于上面出现的错误做一个解释,

代码中,如果去掉了 if(this == NULL) 的检查,并试图在一个空指针上调用 ShowPerson 函数,程序会尝试通过一个空指针来访问 age 成员变量。对空指针进行解引用是一种未定义的行为,通常会导致段错误。

具体步骤如下:

  1. 声明一个空指针,例如 Person* nullPointer = nullptr;
  2. 尝试在这个空指针上调用一个成员函数:nullPointer->ShowPerson();
  3. ShowPerson 函数内部,尝试通过 this->age 访问 age 成员变量。
  4. 由于 this 是一个空指针,尝试访问 this->age 会导致段错误。
4.3.4 const修饰成员函数

常函数:

成员函数后加const后我们称为这个函数为常函数

常函数内不可以修改成员属性

成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:

声明对象前加const称该对象为常对象

常对象只能调用常函数

const修饰成员函数

#include <iostream>class Person {
public://默认构造函数Person() {p_a = 0;p_b = 0;} void ShowPerson() {//隐含在每个成员函数内部都有一个this指针,this是一个常量指针//this = NULL; 常量指针的内容不能修改this->p_a = 10;//对于常量指针的指向的内容可以修改       } void ShowPerson2() const {//隐含在每个成员函数内部都有一个this指针,this是一个常量指针//this = NULL; 常量指针的内容不能修改//const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量//this->p_a = 20;//p_b变量被mutable修饰,可以修改this->p_b = 100;//常函数中变量不能修改//p_a = 12;//使用mutable修改的变量可以修改p_b = 22;       } public:int p_a;mutable int p_b;};void test01()
{   //创建一个对象,会调用默认构造函数Person p1;//查看变量p_a和p_b的值,调用构造函数后值都为0std::cout << "p_a:" << p1.p_a << " p_b:" << p1.p_b << std::endl;//使用对象调用成员函数p1.ShowPerson();//查看此时变量p_a和p_b的值,p_a会变为10,p_b为0std::cout << "p_a:" << p1.p_a << " p_b:" << p1.p_b << std::endl;//使用对象调用常量成员函数p1.ShowPerson2();//查看此时变量p_a和p_b的值,p_a会为10,p_b为22std::cout << "p_a:" << p1.p_a << " p_b:" << p1.p_b << std::endl;}int main() {test01();return 0;
}

运行结果如下图:

image-20231220151754139

const修饰对象

#include <iostream>class Person {
public://默认构造函数Person() {p_a = 0;p_b = 0;} void ShowPerson() {//隐含在每个成员函数内部都有一个this指针,this是一个常量指针//this = NULL; 常量指针的内容不能修改this->p_a = 10;//对于常量指针的指向的内容可以修改       } void ShowPerson2() const {//隐含在每个成员函数内部都有一个this指针,this是一个常量指针//this = NULL; 常量指针的内容不能修改//const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量//this->p_a = 20;//p_b变量被mutable修饰,可以修改this->p_b = 100;//常函数中变量不能修改//p_a = 12;//使用mutable修改的变量可以修改p_b = 22;       } public:int p_a;mutable int p_b;};void test01()
{   //创建一个常量对象p1,会调用默认构造函数const Person p1;//1 常量对象不能修改成员变量的值//p1.p_a = 33;//可以修改被mutable修饰的变量值p1.p_b = 11;//可以访问成员变量的值std::cout << "p_a:" << p1.p_a << std::endl;std::cout << "p_b:" << p1.p_b << std::endl;//2 常量对象访问成员函数,只能访问常函数//ShowPerson是非常函数,常量对象不能访问//p1.ShowPerson();//访问常函数p1.ShowPerson2();std::cout << "p_a:" << p1.p_a << " p_b:" << p1.p_b << std::endl;}int main() {test01();return 0;
}

运行结果如下:

image-20231220161615196
= 11;
//可以访问成员变量的值
std::cout << “p_a:” << p1.p_a << std::endl;
std::cout << “p_b:” << p1.p_b << std::endl;

//2 常量对象访问成员函数,只能访问常函数//ShowPerson是非常函数,常量对象不能访问
//p1.ShowPerson();
//访问常函数
p1.ShowPerson2();
std::cout << "p_a:" << p1.p_a << " p_b:" << p1.p_b << std::endl;

}

int main() {

test01();return 0;

}


运行结果如下:[外链图片转存中...(img-kmQfEuy2-1703064535983)]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/221346.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AXure的情景交互

目录 导语&#xff1a; 1.erp多样性登录界面 2.主页跳转 3.省级联动​编辑 4. 下拉加载 导语&#xff1a; Axure是一种流行的原型设计工具&#xff0c;可以用来创建网站和应用程序的交互原型。通过Axure&#xff0c;设计师可以创建情景交互&#xff0c;以展示用户与系统的交…

JavaAwtSwing的JFrame的pack()方法,容器适配子组件大小,笔记231220

pack()是extends自Window类的方法 使此窗口的大小适合其子组件的首选大小和布局。如果其中一个尺寸小于上一次调用setMinimumSize方法指定的最小尺寸&#xff0c;则会自动放大窗口的宽度和高度。 如果窗口和/或其所有者还不可显示&#xff0c;则在计算首选大小之前&#xff0…

大数据机器学习:从理论到实战,探索学习率的调整策略

大数据机器学习&#xff1a;从理论到实战&#xff0c;探索学习率的调整策略 全文目录 大数据机器学习&#xff1a;从理论到实战&#xff0c;探索学习率的调整策略一、引言二、学习率基础定义与解释学习率与梯度下降学习率对模型性能的影响 三、学习率调整策略常量学习率时间衰减…

如何入门 GPT 并快速跟上当前的大语言模型 LLM 进展?

入门GPT 首先说第一个问题&#xff1a;如何入门GPT模型&#xff1f; 最直接的方式当然是去阅读官方的论文。GPT模型从2018年的GPT-1到现在的GPT-4已经迭代了好几个版本&#xff0c;通过官方团队发表的论文是最能准确理清其发展脉络的途径&#xff0c;其中包括GPT模型本身和一…

迪文屏开发保姆级教程——页面键盘

迪文屏页面键盘保姆级教程。 本篇文章主要介绍了在DGBUS平台上使用页面键盘的步骤。 迪文屏官方开发指南PDF&#xff1a;&#xff08;不方便下载的私聊我发给你&#xff09; https://download.csdn.net/download/qq_21370051/88647174?spm1001.2014.3001.5503https://downloa…

浅析RoPE旋转位置编码的远程衰减特性

为什么 θ i \theta_i θi​的取值会造成远程衰减性 旋转位置编码的出发点为&#xff1a;通过绝对位置编码的方式实现相对位置编码。 对词向量 q \boldsymbol{q} q添加绝对位置信息 m m m&#xff0c;希望找到一种函数 f f f&#xff0c;使得&#xff1a; < f ( q , m ) …

深度学习中的张量维度

1 深度学习中的张量 在深度学习框架中&#xff0c;Tensor&#xff08;张量&#xff09;是一种数据结构&#xff0c;用于存储和操作多维数组。张量可以被视为一种扩展的矩阵&#xff0c;它可以具有任意数量的维度。 在深度学习中&#xff0c;张量通常被用来表示神经网络的输入…

Java 栈和队列的交互实现

文章目录 队列和栈的区别一.用队列模拟实现栈1.1入栈1.2出栈1.3返回栈顶元素1.4判断栈是否为空 二.用栈模拟实现队列2.1 入队2.2出队2.3peek2.4判断队列是否为空 三.完整代码3.1 队列模拟实现栈3.2栈模拟实现队列 队列和栈的区别 栈和队列都是常用的数据结构&#xff0c;它们的…

解读远程工作设计师之未来与发展

引言 在数字化的浪潮下&#xff0c;“远程工作”已经成为现代职场的一个重要趋势。对于设计师来说&#xff0c;这不仅是一种工作方式的转变&#xff0c;更是职业发展的新机遇。在这篇文章中&#xff0c;我将从以下9个方面&#xff0c;深入探讨远程工作设计师的机会、市场和职位…

2023ChatGPT浪潮,2024开源大语言模型会成王者?

《2023ChatGPT浪潮&#xff0c;2024开源大语言模型会成王者&#xff1f;》 一、2023年的回顾 1.1、背景 我们正迈向2023年的终点&#xff0c;回首这一年&#xff0c;技术行业的发展如同车轮滚滚。尽管互联网行业在最近几天基本上处于冬天&#xff0c;但在这一年间我们仍然经…

如何开发一个prompt?prompt的使用有哪些原则?

提示词使用原则 如何开发一个跟自己预期结果接近的提示词&#xff1f;有哪些基本原则&#xff1f; 提示词迭代开发 写提示词时&#xff0c;第一次尝试是值得的&#xff0c;反复完善提示&#xff0c;获得越来越接近你想要的结果 原文来源于B站吴恩达提示工程教学公开课。…

TensorFlow(2):Windows安装TensorFlow

1 安装python环境 这一步请自行安装&#xff0c;这边不做介绍。 2 安装anaconda 下载路径&#xff1a;Index of /&#xff0c;用户自行选择自己的需要的版本。 3 环境配置 3.1 anaconda环境配置 找到设置&#xff0c;点击系统->系统信息->高级系统设置->环境变量…

【VScode和Leecode的爱恨情仇】command ‘leetcode.signin‘ not found

文章目录 一、关于command ‘leetcode.signin‘ not found的问题二、解决方案第一&#xff0c;没有下载Nodejs&#xff1b;第二&#xff0c;有没有在VScode中配置Nodejs第三&#xff0c;力扣的默认在VScode请求地址中请求头错误首先搞定配置其次搞定登入登入方法一&#xff1a;…

Kafka-Kafka核心参数详解

Kafka的HighLevel API使用是非常简单的&#xff0c;所以梳理模型时也要尽量简单化&#xff0c;主线清晰&#xff0c;细节慢慢扩展。 Kafka提供了两套客户端API&#xff0c;HighLevel API和LowLevel API。 HighLevel API封装了kafka的运行细节&#xff0c;使用起来比较简单&…

静态路由及动态路由

文章目录 静态路由及动态路由一、静态路由基础1. 静态路由配置2. 负载分担3. 路由备份4. 缺省路由5. 静态路由实操 二、RIP 动态路由协议1. RIP 协议概述2. RIP 协议版本对比2.1 有类路由及无类路由 3. RIP 路由协议原理4. RIP 计时器5. 度量值6. 收敛7. 示例 静态路由及动态路…

【K8s】#1使用kuboard-spray安装K8s集群

文章目录 搭建k8s集群1.推荐配置1.1.服务器配置1.2.软件版本 2.使用Kuboard-Spray安装k8s集群2.1.配置要求2.2.操作系统兼容性2.3.安装 Kuboard-Spray2.4.加载离线资源包2.5.规划并安装集群2.6.安装成功2.7.访问集群 3.涉及的命令3.1.linux 4.问题汇总Q1&#xff1a;启动离线集…

【Android Studio】各个版本下载地址

下载地址&#xff1a; https://developer.android.com/studio/archive?hlzh-cn

无人机在融合通信系统中的应用

无人驾驶飞机简称“无人机”&#xff0c;是利用无线电遥控设备和自备的程序控制装置操纵的不载人飞行器&#xff0c;现今无人机在航拍、农业、快递运输、测绘、新闻报道多个领域中都有深度的应用。 在通信行业中&#xff0c;无人机广泛应用于交通&#xff0c;救援&#xff0c;消…

Flutter实现丝滑的滑动删除、移动排序等-Dismissible控件详解

文章目录 Dismissible 简介使用场景常用属性基本用法举例注意事项 Dismissible 简介 Dismissible 是 Flutter 中用于实现可滑动删除或拖拽操作的一个有用的小部件。主要用于在用户对列表项或任何其他可滑动的元素执行删除或拖动操作时&#xff0c;提供一种简便的实现方式。 使…

聚类算法及可视化方法的实践与探索

簇内平方和表示数据点到其簇内质心的距离的平方和&#xff0c;公式如下&#xff1a; 其中&#xff0c; 是k簇数&#xff0c; ni是第 i 个簇的样本数&#xff0c; xij是第 i个簇中的第 j 个样本。 import matplotlib.pyplot as plt from sklearn.cluster import KMeans from sk…