【类与对象(1)】类的引入、访问及封装、定义、作用域、实例化、类大小的计算、this指针

1.类的引入

 C++兼容了C语言结构体的用法,但是同时又升级成了类。结构体中只能定义变量,类中不仅可以定义变量,还可以定义函数

例如,数据结构中实现栈,结构体stack中只定义了变量,要实现的函数在结构体外定义。以下代码就是对该实例的实现以及使用。

//C语言中结构体实现
struct stack
{
	int* a;
	int top;
	int capacity;
};
void StackInit(struct stack* s,int capacity = 4)
{
	int* tmp = (int*)malloc(sizeof(int) * capacity);
	if (tmp == nullptr)
	{
		perror("malloc fail");
		return;
	}
	s->a = tmp;
	s->capacity = capacity;
	s->top = 0;
}
void StackPush(struct stack* s, int x)
{
	//扩容
	s->a[s->top++] = x;
}
//C++中对结构体升级为类后实现
struct stack
{
	//成员变量
	int* a;
	int top;
	int capacity;
	//成员函数
	void StackInit(int icapacity = 4)
	{
		int* tmp = (int*)malloc(sizeof(int) * icapacity);
		if (tmp == nullptr)
		{
			perror("malloc fail");
			return;
		}
		a = tmp;
		capacity = icapacity;
		top = 0;
	}
	void StackPush(int x)
	{
		//扩容
		a[top++] = x;
	}
};
int main()
{
	struct stack s;
	StackInit(&s);
	StackPush(&s, 1);
    //类名可以代表类型
	stack1 s1;
	s1.StackInit();
	s1.StackPush(1);
}

C++中更喜欢用 class 来定义类。

2.类的访问限定符及封装

2.1访问限定符

请看下面的代码,思考结果是什么。

class Date
{
	int _year;
	int _month;
	int _day;
	void Init(int year, int month, int day)
	{
		_year = year;;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
};

int main()
{
	Date s;
	s.Init(2003, 10, 1);
	s.Print();
	return 0;
}

是在屏幕上打印出 2003/10/1 吗?

不是的,反而会报错。显示函数 Init 和函数 Print 不可访问。这是为什么呢?

因为访问限定符的存在。

访问限定符也是C++实现封装的方式:用类将对象的属性与方法结合在一块,将对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。

【访问限定符的说明】

①public修饰的成员在类外可以被直接访问;

②private和protected修饰的成员在类外不能直接被访问

③访问权限作用域从该访问限定符出现位置开始直到下一个访问限定符出现时为止,如果后面没有访问限定符,作用域就到}即类结束。

class的默认访问权限为private,struct的默认访问权限是public(因为struct要兼容C)

要想让刚才的代码能够正确运行,应该对成员的访问权限进行设定。虽然class的默认访问权限是private,不过为了更清晰便将其标注出来。一般对于成员变量,只允许类内访问,将其设置为private。

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	void Init(int year, int month, int day)
	{
		_year = year;;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
};

 2.2封装

面向对象的三大特性:封装、继承、多态

 封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互

封装本质上是一种管理,让用户更方便使用类。

在C++中实现封装,可以通过类将数据及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接使用

3.类的定义

类定义的格式:

class  classname

{

    // 类体:由成员函数和成员变量组成

};  // 一定要注意后面的分号

class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面
号不能省略
类体中内容称为类的成员:类中的变量称为类的属性成员变量; 类中的函数称为类的方法或者
成员函数

类有两种定义方式:

①声明和定义全部放在类体中。成员函数如果在类中定义,编译器可能会当将其作内联函数处理。这种方式是比较简单的。

②成员函数声明与定义分离。

这样实现的结果是不对的,报错显示未声明的标识符。因为在未指定作用域的情况下,搜索原则是先在局部区域查找,没找到再从全局域查找。而上面形式中 Init函数作用域内没有声明_year等变量,全局域中也未声明,所以会出错。

正确形式应是在定义时在成员函数前加类名::

4.类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类外定义成员时,需要使用::作用域操作符指明成员属于哪个类域。

class Person
{
private:
	char _name[20];
	char _gender[3];
	int _age;
public:
	void PrintPersonInfo();
};
//指定函数PrintPersonInfo属于Person这个类域
void Person::PrintPersonInfo()
{
	cout << _name << " " << _gender << " " << _age << endl;
}

5.类的实例化

类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。

一个类可以实例化出多个对象。

圈出的部分是变量的声明还是定义呢? 

声明。变量的定义和声明有一个区别,变量定义需要开辟空间。那么该部分应该怎样定义?

 

也不可以通过下面的方式对其进行访问,因为它只是对_yaer等的声明,通过类域Date::找到了_year的出处,但是并没有空间。只能通过定义的类对象去访问,如s._year++;

6.类对象模型

6.1如何计算类对象的大小

class Date
{
public:
	int _year;
	int _month;
	int _day;

	void Init(int year, int month, int day)
	{
		_year = year;;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
};
int main()
{
	Date s;
	cout << sizeof(s) << endl;
	return 0;
}

结果是12,根据结果看好像是三个成员变量在内存中所占的字节数。可是类中既可以有成员变量,也可以有成员函数,那么一个类的对象中到底包含了什么?如何计算类的大小?

先看以下代码,看看两个_year是同一空间吗,两个Init函数地址一样吗?

int main()
{
	Date s1;
	Date s2;
	s1._year = 2000;
	s2._year = 2000;

	s1.Init(2003, 10, 1);
	s2.Init(2003, 10, 1);
	return 0;
}

 通过汇编语言可以看出,s1._year和s2._year占用了不同空间,而s1.Init和s2.Init是相同的地址。

6.2类对象存储方式的猜测

对象中包含类的各个成员

 这样设计是可以的,但是有缺陷。

缺陷:每个对象中的成员是不同的,但是调用的是同一份函数,按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码,相同的代码保存多次,浪费空间。

只保存成员变量,成员函数存放在公共的代码段

显然在以上两种存储方式中,计算机是按照第二种来存储的。

结论:

一个类的大小,实际就是该类中“成员变量”之和,当然要注意内存对齐;

当类中只有成员函数时,类大小也是1,为了占位,标识对象实例化时,定义出来存在过;

注意空类的大小,空类大小并不是0,编译器给了空类一个字节来唯一标识这个类的对象。

6.3类大小计算规则

遵循结构体对齐规则

对齐规则:

1. 第一个成员在与结构体变量偏移量为0的地址处。

2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。

   //VS中默认的值为8 

   //Linux中没有对齐数

3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

7.this指针

7.1引入 

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1, d2;
	d1.Init(2022, 1, 11);
	d2.Init(2022, 1, 12);
	d1.Print();
	d2.Print();
	return 0;
}

以上定义了一个Date类。我们知道类对象实例化后成员变量在其空间内,成员函数在一片公共区域,所以函数体内没有对不同对象的区分,那么当函数Init被d1对象调用时,被调用函数如何知道应该设置为d1而不是d2呢?这就引入了this指针来解决这个问题。

C++ 编译器给每个 非静态的成员函数 增加了一个隐藏  的指针参数,让该指针指向当前对象 ( 函数运行时调用该函数的对象 ) ,在函数体中所有 成员变量 的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编 译器自动完成

7.2this指针详解

        所有的成员函数都会多一个指针,这个指针就是this指针。它是成员函数的第一个参数,可以想象成下面的形式

 与我们对栈进行初始化相同,函数传参时将定义的栈对象地址传过去,用指针接收。

所以this指针并不难理解,但它又有自己的一些特性:

①C++规定:把this指针叫做隐含的this指针,所有的成员函数第一个参数都是它,一般情况由编译器通过ecx寄存器自动传 递,不需要用户传递;

②this指针的类型:类类型* const ,即成员函数中,不能给this指针赋值;

③在形参和实参的位置,不能显示写出

④在函数内部可以使用

this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针

面试中常见问题

1.this指针存在哪里?
是以下哪个选项呢
        a、堆        b、栈        c、静态区        d、常量区        e、对象内
首先能够排除的是e,this指针不可能存在对象内。this指针本质上是“成员函数”的形参 ,当对象调用成员函数时,将对象地址作为实参传递给 this形参。所以对象中不存储this指针
通过malloc的变量在堆上,所以也排除了a;
const修饰的变量不是就一定在常量区,如下面代码

 i虽然被const修饰,但 i 和 j 地址连续,是局部变量,都放在栈上;p是指针,也在栈上,但指针p所指的字符串在常量区;排除d;

static和全局变量在静态区,排除c;
所以this指针存储在栈上!因为它是一个形参。函数调用会建立栈帧,局部变量存在在函数栈帧中,即函数形参在栈区。
注意有些编译器可能会采用寄存器存储,如VS。


2.this指针可以为空吗?
(1)看以下代码,它的编译运行结果是什么呢?
A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
	void Print()
	{
		cout << "Print()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

结果是C、正常运行,运行结果如图。

为什么是能正常运行的?p是空指针,对空指针解引用应该是会报错的呀,但是上面的操作却可以正常运行。
-> 不一定存在解引用,这取决于后面所跟的值在不在指针所指向的空间里面。p是指向对象的指针,Print是类的成员函数,我们在类对象存储模型中讲过计算机采用的存储方式是 只保存成员变量,成员函数存放在公共的代码段,即Print函数不在p所指的空间内,在编译时就确定了Print函数的地址,也就是不存在对空指针的解引用,所以 正常运行
(2)看以下代码,它的编译运行结果是什么呢?
A、编译报错 B、运行崩溃 C、正常运行
class A
{ 
public:
    void PrintA() 
   {
        cout<<_a<<endl;
   }
private:
 int _a;
};
int main()
{
    A* p = nullptr;
    p->PrintA();
    return 0;
}

结果是B、运行崩溃出现了this指针为空的情况,如下图

p是定义的指向类A实例化后的空指针,p->PrintA()中传的参数为空指针,PrintA函数的形参this指针接收空值。
cout << _a <<endl; 相当于 cout << this->_a << endl; ,_a存在与this指针所指的空间, this->_a 发生了对空指针的解引用,产生了运行错误。
以上就是对类与对象的初步认识,新的知识我们下篇博客见~(感谢观看,有不对的地方麻烦指出)

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

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

相关文章

FastAI 之书(面向程序员的 FastAI)(一)

原文&#xff1a;www.bookstack.cn/read/th-fastai-book 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 序言 原文&#xff1a;www.bookstack.cn/read/th-fastai-book/README.md 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 在很短的时间内&#xff0c;深度…

建筑工程如何找答案? #职场发展#笔记#职场发展

这些软件以其强大的搜索引擎和智能化的算法&#xff0c;为广大大学生提供了便捷、高效的解题方式。下面&#xff0c;让我们一起来了解几款备受大学生欢迎的搜题软件吧&#xff01; 1.易解题 这是一个网站 是我在百度搜题&#xff0c;经常会出现的一个网站&#xff0c;它里面…

K8S集群实践之十:虚拟机部署阶段性总结

目录 1. 说明&#xff1a; 2. 安装准备 2.1 每个节点设置双网卡&#xff0c;一卡做网桥&#xff08;外部访问&#xff09;&#xff0c;一卡做NAT&#xff08;集群内网访问&#xff09; 2.2 准备一个可用的代理服务器 3. 由于虚拟机崩溃&#xff08;停电&#xff0c;宿主机…

Gemini 1.5 Pro揭秘:Google DeepMind新一代AI模型如何突破千万级别词汇限制?

Gemini 1.5 Pro 发布&#xff01; 这款模型凭借其超长的上下文处理能力脱颖而出&#xff0c;支持10M tokens。 它的多模态特性意味着&#xff0c;无论面对多么庞大复杂的内容&#xff0c;Gemini 1.5 Pro都能游刃有余地应对。 在AI的世界里&#xff0c;上下文的理解如同记忆的…

如何创建和填写 PDF 表单,简化您的文档工作流

阅读本文&#xff0c;了解如何在开源办公套件 ONLYOFFICE 中创建和填写 PDF 表单。 ONLYOFFICE表单发展小史 ONLYOFFICE 表单首个版本发布于2022年1月18日&#xff0c;是 ONLYOFFICE 版本 7.0 更新的一部分。 您可以使用 ONLYOFFICE 表单&#xff0c;创建各种类型的模板文档&a…

DS Wannabe之5-AM Project: DS 30day int prep day18

Q1. What is Levenshtein Algorithm? Levenshtein算法&#xff0c;也称为编辑距离算法&#xff0c;是一种量化两个字符串之间差异的方法。它通过计算将一个字符串转换成另一个字符串所需的最少单字符编辑操作次数来实现。这些编辑操作包括插入、删除和替换字符。Levenshtein距…

备战蓝桥杯---图论之最短路Bellman-Ford算法及优化

目录 上次我们讲到复杂度为&#xff08;nm)logm(m为边&#xff0c;n为点&#xff09;的迪杰斯特拉算法&#xff0c;其中有一个明显的不足就是它无法解决包含负权边的图。 于是我们引进Bellman-Ford算法。 核心&#xff1a;枚举所有的点&#xff0c;能松弛就松弛&#xff0c;直…

使用radial-gradient完成弧形凹陷的绘制

1、效果如下图 我在微信小程序中制作的 2、代码如下 <style>.header {position: relative;width: 200px;height: 200px;overflow: hidden;}.header .circle {--circleValue: 500px;position: absolute;bottom: 0;left: 50%;width: 100%;height: var(--circleValue);trans…

2024牛客寒假算法基础集训营2-c Tokitsukaze and Min-Max XOR

来源 题目 Tokitsukaze 有一个长度为 n 的序列 a1,a2,…,an和一个整数 k。 她想知道有多少种序列 b1,b2,…,bm满足&#xff1a; 其中 ⊕\oplus⊕ 为按位异或&#xff0c;具体参见 百度百科&#xff1a;异或 答案可能很大&#xff0c;请输出  mod1e97 后的结果。 输入描述…

【开源】SpringBoot框架开发食品生产管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 加工厂管理模块2.2 客户管理模块2.3 食品管理模块2.4 生产销售订单管理模块2.5 系统管理模块2.6 其他管理模块 三、系统展示四、核心代码4.1 查询食品4.2 查询加工厂4.3 新增生产订单4.4 新增销售订单4.5 查询客户 五、…

基于微信小程序的智能社区服务小程序,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

【STM32】OLED显示实验

目录 一、硬件介绍 1. STM32F03C8T6 2. OLED屏 二、STM32CubeMX配置 1. 实际接线 2. IIC配置 3. 其他配置 四、字模生成 1. 打开PCtoLCD2002.exe软件。选择字符模式。 2. 以16x16汉字取模为例&#xff0c;即字宽字高都为16. 3. 输入取模汉字。 4. 点击生成自模。 …

毕业设计vue+php幼儿园网站系统yl567

幼儿园网站系统。采用vscode集成IDE对幼儿园网站系统统进行开发,整合系统的各个模块。 拟开发的幼儿园网站系统通过测试,确保在最大负载的情况下稳定运转,各个模块工作正常,具有较高的可用性。系统整体界面简洁美观,用户使用简单,满足用户需要。在因特网发展迅猛的当今社会,幼儿…

最短路径与关键路径

目录 文章目录 前言 一.最短路径 1.基本概念 1.1什么是源点&#xff1f; 1.2什么是最短路径 2.作用 3.迪杰斯特拉算法 4. 弗洛伊德算法 4.1过程演示 二.拓扑排序 1.基本概念 1.1什么是有向无环图 1.2什么是活动 1.3什么是AOV网 1.4什么是拓扑序列 1.5什么是拓扑…

多线程练习

做以下练习的基本步骤&#xff1a; 定义共享变量&#xff08;若要创建多个对象使用静态&#xff09;&#xff1b;写循环&#xff08;while&#xff09; 有时候不写while&#xff0c;比如说抢红包每条线程只能抢一次&#xff0c;就不用while循环 同步代码块判断&#xff08;已经…

OpenSource - 一站式自动化运维及自动化部署平台

文章目录 orion-ops 是什么重构特性快速开始技术栈功能预览添砖加瓦License orion-ops 是什么 orion-ops 一站式自动化运维及自动化部署平台, 使用多环境的概念, 提供了机器管理、机器监控报警、Web终端、WebSftp、机器批量执行、机器批量上传、在线查看日志、定时调度任务、应…

《合成孔径雷达成像算法与实现》Figure6.17

% rho_r c/(2*Fr)而不是rho_r c/(2*Bw) % Hsrcf exp函数里忘记乘pi了 clc clear close all参数设置 距离向参数设置 R_eta_c 20e3; % 景中心斜距 Tr 2.5e-6; % 发射脉冲时宽 Kr 20e12; % 距离向调频率 alpha_os_r 1.2; …

B端系统从0到1:有几步,其中需求分析要做啥?

一款B系统从无到有都经历了啥&#xff0c;而其中的需求分析又要做什么&#xff1f;贝格前端工场给老铁们做一下分析&#xff0c;文章写作不易&#xff0c;如果咱们有界面设计和前端开发需求&#xff0c;别忘了私信我呦&#xff0c;开始了。 一、B端系统从0到1都有哪些要走的步骤…

绝地求生:“觉醒之旅”通行证曝光,西游主题通行证及成长型武器即将上线

随着27赛季即将结束&#xff0c;有关28.1版本的皮肤及通行证内容也被爆料出来&#xff0c;本次通行证为工坊通行证&#xff0c;和去年四圣兽通行证为同一类型&#xff0c;将于2月7日更新至正式服 除了通行证获取工坊币还是可以开箱获取并兑换一些奖励 先看通行证 四个套装应该分…

如何给最小化安装的CentOS主机装个远程桌面?

正文共&#xff1a;888 字 18 图&#xff0c;预估阅读时间&#xff1a;1 分钟 前面我们领微软云Azure的免费主机时&#xff08;白嫖党618福利&#xff01;来Azure领200美刀&#xff01;外加云主机免费用一年&#xff01;&#xff09;&#xff0c;发现“有资格免费试用服务”的主…
最新文章