【逐步剖C++】-第一章-C++类和对象(上)

news2024/11/23 10:01:25

前言:本文主要介绍有关C++入门需掌握的基础知识,包括但不限于以下几个方面,这里是文章导图:
在这里插入图片描述

本文较长,内容较多,大家可以根据需求跳转到自己感兴趣的部分,希望能对读者有一些帮助
那么本文也主要以导图为思路进行分享,话不多说,让我们开始吧。

一、关于类的理解与意义

1、理解

什么是类呢?可以直接根据其表面来理解。这个“类”我们常说的给事物分类的“类”,比如,动物类,交通工具类等等。通过给不同的事物进行分类,能让我们更有条理性、更系统地认知这个世界。同样的道理,类的设计可以让我们的代码编写更具逻辑性、可维护性等等。以上就是对类的简单理解。

2、意义

那么类的设计有什么意义呢?基本的意义上述也提到了,能增强代码的可维护性等,它能将具有协同功能代码封装成一个整体,从而更好地进行管理;更多对其意义的感知还是需要通过不断的学习,和代码实践来逐步加强。

关于类和对象的更形象理解方式大家感兴趣地可以去搜搜文章和书籍,本文主要还是以学习笔记的方式来展现知识内容,这里就不再赘述。

二、类的定义方式

1、关于兼容C后的结构体

由于C++兼容C,所以在C++中结构体被升级成了类。在C中的结构体只能定义成员变量,升级成类后也可以定义成员函数。但二者仍有一些区别,下文会继续进行说明。

2、定义方式

类的定义方式如下:

class typename
{

};

其中class为定义类的关键字;typename为用于定义该类的类名;花括号中为类的具体内容;最后别忘了分号。

在实际工程中定义类时,我们可以根据需求将类声明(主要是其中成员函数的声明)放在头文件中,在.cpp文件中再对类中的成员函数进行定义。

三、类的访问限定符及封装的理解

类访问限定符是C++实现封装的主要方式。通过访问限定符,可以选择性地将类中的成员展现给外界用户。和平常被我们使用的电子产品一样,都是将简单易用的功能展现给我们,而将复杂的实现细节封装起来

1.类的访问限定符

(1)类的访问限定符一共有三种,分别是:publicprotectedprivate
对以上三种访问限定符的说明如下:

  • 被pubilc修饰的成员能在类外直接访问
  • 被protected和private修饰的成员不能在类外直接访问,(PS:关于protected和private的区别在继承章节会提到,平常使用可认为二者类似
  • 访问限定符的作用范围是:从当前访问限定符开始到下一个访问限定符出现;若下一个访问限定符一直未出现,则右花括号为止。
  • class关键字定义的类默认的访问权限为private;struct关键字定义的类的默认范围权限为public(兼容C)

(2)类访问限定符在程序生命周期中作用的阶段:编译阶段。即访问限定符只在编译阶段有用,当数据映射到内存后,没有任何访问限定符上的区别。

2、对封装的理解

封装是面向对象的三大特性之一(封装、继承、多态)。对封装的理解其实很简单,前文也有谈到,其实就是将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,对外仅公开使用工具(接口)来和对象进行交互。本质上属于一种管理,让用户更方便的使用类,也让类的设计更加规范化。

四、类的作用域与实例化

1、类的作用域

类的作用域也属于“域”的一种,至此我们所认识的“域”,一共就有如下三种:

  • 局部作用域
  • 全局作用域
  • 命名空间域
  • 类作用域

在笔者的另一篇文章【逐步剖C++】-第一章-C++入门知识的命名空间部分中,提到了用域访问符::访问命名空间,这里我们也有该符号的使用需求,请看:
在类外定义成员函数时,需使用域访问符指明成员函数属于哪个类域,请看下面代码例:

class test
{
public:
	void testFunc();
}

void test::testFunc()
{
	//...
}

2、类的实例化

类的实例化,本质就是通过类创建一个对象,实例化出来的对象占实际的物理空间,存储类的成员变量。由此可见,类在没有实例化之前,系统并没有为其成员变量并开实际的空间,在这个阶段成员变量仅是声明;当进行类的实例化时,成员变量才得到整体定义。

这里有一个形象的理解方式:
类的实例化过程就像拿着建筑设计图造出房子的过程。类就好比设计图,仅对房子中的内容、布局等进行规划与设计,并没有实体的建筑存在。所以,仅有一个类的定义本质是不占空间的,只有实例化出对象后,才占物理空间而实际存储数据;同理也不能直接在类外对类中声明的成员变量进行赋值,就好比设计图中不能直接住人

五、类对象的大小计算

这里通过三个类的例子进行对类对象的大小计算的说明:

// 类中既有成员变量,又有成员函数
class A1 {
public:
void f1(){}
private:
int _a;
};

// 类中仅有成员函数
class A2 {
public:
void f2() {}
};

// 类中什么都没有---空类
class A3
{};

上面三个类实例化出的对象的大小分别为4字节、1字节、1字节。
解释:
在类中,对于成员变量成员函数的大小计算不同:

  • 对于成员变量,相同类实例化出的每个对象的成员变量是不一样的,就好比同一张设计图造出来的房子都有卧室,但这两个卧室一定是不同的;计算它们的大小时,和结构体一样,采用内存对齐的方法进行计算即可。关于内存对齐,可以看看笔者这篇文章:【逐步剖C】-第十章-自定义类型之结构体、枚举、联合
  • 对于成员函数,相同类实例化出的每个对象其实是一样的,成员函数就像是篮球场、健身房等场所,不用每家都建而建成公共的,这样就避免了由于重复而引起的空间浪费,那么类中的成员函数存储方式类似,其会以成员函数表的形式放在一个公共的代码段中,当类的对象需要调用成员函数时,就到这个公共代码段中找对应的函数地址来完成调用,其实际不在类中(不占空间)。

验证

class A
{
public:
	void Test()
	{
		cout << "void Test()" << endl;
		cout << _a << endl;
	}
private:
	int _a = 0;		//成员变量缺省值,后续章节会说明
};
int main()
{
	A a1;
	A a2;
	a1.Test();
	a2.Test();
}

运行后转到反汇编:
在这里插入图片描述
可以看到,同一个类的两个不同对象所调用的成员函数的地址相同PS:图中红框就是代码中的Test函数,蓝框是A类的默认构造函数,后续章节会进行说明)。

那么在最开始的代码例中,类A1的大小即为其成员变量的大小;类A2和类A3的大小类似,编译器会给他们一个字节来唯一标识这个类的对象。

六、this指针

1、this指针的本质

场景引入:在上面的例子中,我们通过类A实例化了两个对象,并且这两个对象都调用了成员函数Test(),那么编译器是如何知道是哪个对象调用的呢?这就是this指针解决的问题了。

C++编译器给每个非静态的成员函数增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),指针中的内容即为对象本身的地址,在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
即例子中两条调用语句其实可理解为:

a1.Test(&a1);
a2.Test(&a2);

需要注意的是,如上仅作为一种理解方式,实际不能这么写,因为编译器规定this指针不能在实参和形参显示传递,仅可以在成员函数内部显示使用
如:

class A
{
public:
	void Test()
	{
		cout << "void Test()" << endl;
		cout << _a << endl;		//编译器自动处理this指针

		//也可显示使用写为:cout << this->_a << endl;
	}
private:
	int _a = 0;		//成员变量缺省值,后续章节会说明
};

2、this指针的特性

  • this指针的类型其实为const 类的类型*,如上A类的对象的this指针类型就为A* const,即不能改变指针所指向的对象,而可以改变对象中的具体内容(成员变量等);
  • 只能在成员函数中显示调用;
  • this指针本质上是成员函数形参,当对象调用成员函数时,编译器一般通过ecx寄存器自动将对象地址作为实参传递给该形参。故在类的对象中不存储this指针

3、关于this指针的一些问题

了解了this指针的特性后,下面有一些常被提起的关于this指针的问题,请看:

  • this指针可以为空吗
    可以通过下面这一段代码来认识并解决这个问题:
class A
{

public:
	void Print()
	{
		cout << "Print()" << endl;
	}

	void PrintA()
	{
		cout << _a << endl;
	}
private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->Print();
	p->PrintA();
	return 0;
}

p是一个A类对象的指针,将其赋值为空指针后用以模仿this指针来调用A类中的成员函数。运行结果如下:
在这里插入图片描述
可以看到,第一个函数Print()正常调用,在调用第二个函数时系统崩溃。原因很明显:p是一个空指针,在调用第一个成员函数时,本质上并没有访问该指针所指向位置的内存(对象的具体内容);而在调用第二个成员函数时,程序想访问并输出类的成员变量_a,那么此时的行为就相当于解引用一个空指针而去访问其指向的内容,也就是我们常说的野指针问题,故造成了程序的崩溃。
综上,this指针一般情况下不会为空。

  • this指针存储在哪里
    第二个问题其实在介绍this指针的特性时已给出了答案,this指针本质上是成员函数的形参,那么既然是形参,也就是局部变量,那就一定存储在栈区当中。

本章完。

看完觉得有觉得帮助的话不妨点赞收藏鼓励一下,有疑问或有误地方的地方还请过路的朋友们留个评论,多多指点,谢谢朋友们!🌹🌹🌹

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

时序预测 | MATLAB实现ARMA自回归移动平均模型时间序列预测

时序预测 | MATLAB实现ARMA自回归移动平均模型时间序列预测 目录 时序预测 | MATLAB实现ARMA自回归移动平均模型时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现ARMA时间序列预测&#xff08;完整源码和数据&#xff09; 本程序基于MATLAB的armax函…

【pdf密码】PDF文件带有密码,该如何编辑文件?

打开PDF文件的时候&#xff0c;没有提示带有密码&#xff0c;但是打开文件之后发现没有办法编辑PDF文件&#xff0c;这个是因为PDF文件设置了限制编辑&#xff0c;我们需要将限制取消才能够编辑文件。 那么&#xff0c;我们应该如何取消密码&#xff0c;编辑文件呢&#xff1f…

使用React Hooks实现表格搜索功能

React Hooks是React 16.8版本引入的新特性&#xff0c;它的作用是为函数组件提供了状态管理和副作用处理的能力。 在React之前&#xff0c;函数组件被限制在只能使用无状态的函数组件&#xff0c;无法使用状态和生命周期方法。Hooks的引入解决了这个限制&#xff0c;使得函数组…

华为全光园区商业市场解决方案

随着全球碳中和实践发展&#xff0c;光进铜退是必然发展趋势&#xff0c;园区网络全光化已经成为新一代智慧园区的新名片。相较传统网络方案&#xff0c;全光园区采用光纤下沉&#xff0c;将光纤从弱电机房延伸到每个房间&#xff0c;每个桌面&#xff0c;每个机器&#xff0c;…

txt实现日期计算器前端代码【搬代码】

使用txt文件实现日期计算器 操作步骤&#xff1a; 1.首先在桌面创建txt文本文档 2.打开文本复制下面代码到文本中 3.修改文本的txt为html 4.右键该html文本&#xff0c;打开方式浏览器&#xff0c;结果如下 5.当然也可以作用于idea 此处是所有代码&#xff0c;复制粘贴即…

上手SpringBoot

设置Maven镜像为阿里云 找到Maven的目录所在位置找到conf目录找到settings.xml文件 找到Maven的目录所在位置&#xff1a;去idea 的设置中 直接搜索Maven 找到conf目录 修改Maven本地仓库的地址 地址自定义 修改Maven的镜像为阿里云镜像 <mirror><id>nexus-aliy…

app源代码安全检测的重要性

源代码审计 app软件代码漏洞扫描&#xff08;源代码安全检测&#xff09;是从安全的角度对代码进行安全测试和评估。同时结合丰富的安全知识、编程经验和测试技术&#xff0c;采用静态分析和人工审查的方法&#xff0c;发现代码架构和编码中的安全漏洞&#xff0c;在代码之前将…

LeetCode 297. Serialize and Deserialize Binary Tree【树,DFS,BFS,设计,二叉树,字符串】困难

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

LeetCode 428. Serialize and Deserialize N-ary Tree【树,BFS,DFS】困难

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

华为倒逼苹果?走出舒适圈积极创新,苹果推出首批CIS堆叠式手机

9月9日消息&#xff0c;苹果公司最近推出了iPhone 15系列&#xff0c;这是业内首批搭载CIS堆叠式传感器的手机。这一消息得到了相关业内人士的关注。知名分析师郭明錤认为&#xff0c;尽管美国施加了制裁&#xff0c;在一些方面对苹果的影响是负面的&#xff0c;但华为的归来将…

Spring-MVC的文件上传下载,及插件的使用(让项目开发更节省时间)

目录 一、概述 ( 1 ) 介绍 ( 2 ) 讲述 二、上传 三、下载 四、jrebel的使用 五、多文件上传 给我们带来什么收获 一、概述 ( 1 ) 介绍 Spring MVC的文件上传下载是指在Spring MVC框架中实现文件的上传和下载功能。文件上传是指将本地计算机上的文件上传到服务器端…

一种基于注意机制的快速、鲁棒的混合气体识别和浓度检测算法,配备了具有双损失函数的递归神经网络

A fast and robust mixture gases identification and concentration detection algorithm based on attention mechanism equipped recurrent neural network with double loss function 摘要 提出一个由注意力机制组成的电子鼻系统。首先采用端到端的编码器译码器&#xff…

987. 二叉树的垂序遍历

987. 二叉树的垂序遍历 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a;对中文的支持有点不友好IDEA 原题链接&#xff1a; 987. 二叉树的垂序遍历 https://leetcode.cn/problems/vertical-order-traversal-of-a-binary-tree/descriptio…

2.3 Java中的运算符

运算符&#xff0c;是表示各种不同运算的符号。 运算符按功能分为算术运算符、逻辑运算符、关系运算符、赋值运算符、位运算符等。 1. 算术运算符 算术运算符一般用于对整型数和浮点型数运算。 运算符 运算 范例 结果 正号 3 3 - 负号 b4;-b -4 加 55 10 …

恒运资本:开盘时间是几点到几点?

开盘时刻是指各种商场的正式开端生意时刻&#xff0c;包括股票商场、外汇商场、商品期货商场等。关于出资者来说&#xff0c;了解开盘时刻是十分重要的&#xff0c;由于它直接关系到生意的时刻和机会。本文将从多个角度分析开盘时刻的重要性、不同商场的开盘时刻以及对出资者带…

linux环境部署jmeter并执行测试

下载jmeter和jdk jmeter官网和java-jdk官网下载压缩包文件 jmeter下载地址&#xff1a;点此下载 jmeter Apache JMeter - Download Apache JMeter java-jdk下载地址&#xff1a;点此下载 jdk Java Downloads | Oracle 安装包根据Linux配置进行选择。 上传文件到Linux并解压文…

大厂案例 - 海量分类业务设计的一些思考

文章目录 业务背景描述方案演进v1 扩展字段扩展性需求查询需求 v2 垂直拆分拆分方案存在的问题 v3 三大中心服务 &#xff08;业界最佳实践&#xff09;统一帖子中心服务统一类目属性服务统一检索服务 小结 业务背景描述 一个分类信息平台&#xff0c;有很多垂直品类&#xff…

LeetCode(力扣)47.全排列 IIPython

LeetCode47.全排列 II 题目链接代码 题目链接 https://leetcode.cn/problems/permutations-ii/ 代码 class Solution:def permuteUnique(self, nums):nums.sort() # 排序result []self.backtracking(nums, [], [False] * len(nums), result)return resultdef backtrackin…

【Python】多线程

进程、线程 现代操作系统比如Mac OS X&#xff0c;UNIX&#xff0c;Linux&#xff0c;Windows等&#xff0c;都是支持“多任务”的操作系统。 进程&#xff1a;就是一个程序&#xff0c;运行在系统之上&#xff0c;那么便称之这个程序为一个运行进程&#xff0c;并分配进程ID…

JavaScript学习笔记02

JavaScript笔记02 数据类型详解 字符串 在 JavaScript 中正常的字符串都使用单引号 或者双引号" "包裹&#xff1a;例&#xff1a; 转义字符 在 JavaScript 字符串中也可用使用转义字符&#xff08;参考&#xff1a;详解转义字符&#xff09;&#xff1a;例&…