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() {
    
    //实例化一个对象为p1
    Person 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;
	} 



    //定义一个成员变量,属性为public
    int age;
};


void test01()
{   
    //实例化一个对象为p1,会调用有参构造函数
    Person p1(10);
    //打印p1.age的结果
    std::cout << "p1.age:" << p1.age << std::endl;

    //实例化一个对象为p2
    Person 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的值,调用构造函数后值都为0
    std::cout << "p_a:" << p1.p_a << " p_b:" << p1.p_b << std::endl;

    //使用对象调用成员函数
    p1.ShowPerson();
    //查看此时变量p_a和p_b的值,p_a会变为10,p_b为0
    std::cout << "p_a:" << p1.p_a << " p_b:" << p1.p_b << std::endl;

    //使用对象调用常量成员函数
    p1.ShowPerson2();
    //查看此时变量p_a和p_b的值,p_a会为10,p_b为22
    std::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)]

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SSM整合实战(Spring、SpringMVC、MyBatis)

五、SSM整合实战 目录 一、SSM整合理解 1. 什么是SSM整合&#xff1f;2. SSM整合核心理解五连问&#xff01; 2.1 SSM整合涉及几个IoC容器&#xff1f;2.2 每个IoC容器盛放哪些组件&#xff1f;2.3 IoC容器之间是什么关系&#xff1f;2.4 需要几个配置文件和对应IoC容器关系&…

Redis原理之网络通信协议笔记

目录 1. RESP协议 ​2. 自定义Socket连接Redis 1. RESP协议 2. 自定义Socket连接Redis public class MyRedisClient {static Socket s;static PrintWriter writer;static BufferedReader reader;static Object obj;public static void main(String[] args) {try {// 1.建立连…

uni-app的初使用(附源码学习)

uni-app代码编写&#xff0c;基本语言包括js、vue、css。以及ts、scss等css预编译器。 新建项目等基础指路&#xff1a; 关于uni-app的下载及使用-CSDN博客 1.vue文件 由三个一级节点组成&#xff0c;分别是template、script、style <template> </template><…

设计模式——外观模式(Facade Pattern)

概述 外观模式又称为门面模式&#xff0c;它通过引入一个外观角色来简化客户端与子系统之间的交互&#xff0c;为复杂的子系统调用提供一个统一的入口&#xff0c;降低子系统与客户端的耦合度&#xff0c;且客户端调用非常方便。它是一种对象结构型模式。外观模式结构图如下所示…

ansible的脚本—playbook剧本

目录 一、playbook 1、简介 2、playbook组成部分&#xff1a; 3、如何编写Playbook&#xff1f; 4、语句的横向/纵向写法 二、playbook模版实例&#xff1a; 1、playbook模版&#xff1a; 2、playbook的条件判断&#xff1a; 3、playbook中的循环&#xff1a; 4、循环…

优维科技荣获第二届中国赛宝信息技术应用创新优秀解决方案三等奖

近日&#xff0c;“第二届中国赛宝信息技术应用创新优秀解决方案”评选活动圆满结束。优维科技所提交的《Hyperlnsight超融合持续观测解决方案》、《EasyOps一体化运维平台》从全国近300份申报方案中脱颖而出&#xff0c;荣获2023中国赛宝信息技术应用创新优秀解决方案奖。 本…

【操作系统】什么是进程?

文章目录 进程进程的属性进程的状态挂起 进程 进程是一个可并发执行的具有独立功能的程序关于某个数据集合的执行过程&#xff0c;也是操作系统进行资源分配和保护的基本单位。 进程的属性 结构性&#xff1a; 共享性&#xff1a;同一程序运行于不同数据集合上构成不同的进程…

C++用哈希表封装unordered_set和unordered_map

目录 前言 一、修改kv模型为data模型 1.添加MyUnorderedSet.h和MyUnorderedMap.h 2.修改HashNode 3.修改HashTable 二、普通迭代器 三、const迭代器 四、unordered_map重载operator[] 总结 前言 在上一篇文章中&#xff0c;我们手写了一份哈希表&am…

【图神经网络】在节点分类任务中无特征节点的特征表示

无特征节点的特征表示 节点度数degree pagerank 以pagerank起源的应用场景为例&#xff0c;不是所有的网站都是同等重要的&#xff0c;所以需要根据结构信息对节点进行排序。 直觉上&#xff0c;如果一个网站它有很多链接&#xff0c;它就很重要&#xff0c;举例来说&#…

Java版企业电子招标采购系统源码—鸿鹄电子招投标系统-企业战略布局下的采购寻源

项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&#xff0c;以及审…

C# WPF上位机开发(业务主流程才是核心)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】前面我们说了很多的c# wpf编程技术,里面有控件,有绘图,有数据库,有多线程等技术。但是他们都属于实现的部分,没有和具体的行业进行挂钩,相当于是通用技术部分。这个通用部分一般通过书…

【深度学习】序列生成模型(五):评价方法计算实例:计算BLEU-N得分【理论到程序】

文章目录 一、BLEU-N得分&#xff08;Bilingual Evaluation Understudy&#xff09;1. 定义2. 计算N1N2BLEU-N 得分 3. 程序 给定一个生成序列“The cat sat on the mat”和两个参考序列“The cat is on the mat”“The bird sat on the bush”分别计算BLEU-N和ROUGE-N得分(N1或…

Dubbo面试题及答案,持续更新

在准备Dubbo相关的面试题时&#xff0c;我发现网络上的资源往往缺乏深度和全面性。为了帮助广大Java程序员更好地准备面试&#xff0c;我花费了大量时间进行研究和整理&#xff0c;形成了这套Dubbo面试题大全。 这套题库不仅包含了一系列经典的Dubbo面试题及其详尽答案&#x…

语音识别与人机交互:发展历程、挑战与未来前景

导言 语音识别技术作为人机交互领域的重要组成部分&#xff0c;近年来取得了巨大的发展。本文将深入研究语音识别与人机交互的发展历程、遇到的问题、解决过程、未来的可用范围&#xff0c;以及在各国的应用和未来的研究趋势。我们将探讨在这个领域&#xff0c;哪一方能取得竞争…

CCF编程能力等级认证GESP—C++6级—20230923

CCF编程能力等级认证GESP—C6级—20230923 单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09;判断题&#xff08;每题 2 分&#xff0c;共 20 分&#xff09;编程题 (每题 25 分&#xff0c;共 50 分)小杨买饮料小杨的握手问题 答案及解析单选题判断题编程题1编程题…

微信小程序-选择和分割打开地图选择位置的信息

一、 前言 废话不多说&#xff0c;单刀直入。 本文要实现的功能是微信小程序中打开地图选择位置&#xff0c;以及将返回的位置信息分割。 例如返回的位置信息是&#xff1a;广东省深圳市龙岗区xxxxx小区 分割后变成&#xff1a; {province: "广东省",city: "深…

【蓝桥杯】专题练习

前缀和 3956. 截断数组 - AcWing题库 一看到题目很容易想到的思路是对数组求前缀和&#xff0c;然后枚举两个分段点就好&#xff0c;时间复杂度是On^2&#xff0c;n是1e5会t&#xff0c;需要优化。 朴素的代码&#xff0c;会超时&#xff1a; #include <bits/stdc.h> u…

文件包含 [SWPUCTF 2021 新生赛]include

打开题目 要求我们传入一个file进去&#xff0c;那我们get传入 /?file1 得到源码&#xff0c;并且提示我们flag在flag,php下 在源代码中&#xff0c;我们看见了allow_url_include函数&#xff0c;我们知道这涉及到文件包含。 一般默认allow_url_fopen是on的&#xff0c;那在…

IDEA版SSM入门到实战(Maven+MyBatis+Spring+SpringMVC) -Spring的AOP前奏

第一章 AOP前奏 1.1 代理模式 代理模式&#xff1a;我们需要做一件事情&#xff0c;又不期望自己亲力亲为&#xff0c;此时&#xff0c;可以找一个代理【中介】 我们【目标对象】与中介【代理对象】不能相互转换&#xff0c;因为是“兄弟”关系 1.2 为什么需要代理【程序中…

使用C语言实现文件的拷贝——底层内存分析

使用C语言实现文件的拷贝 本文主要涉及sprintf&#xff08;&#xff09;函数的讲解以及系统IO与标准IO的区别和一个实例使用C语言实现文件的拷贝&#xff0c;在最后还深度刨析了文件拷贝的底层原理。 文章目录 使用C语言实现文件的拷贝一、 sprintf()函数1.1 sprintf ()函数的参…