C++好难(2):类和对象(上篇)

okay,从这里开始,就进入c++比较难的部分了~啊啊啊!!! (¯﹃¯ԅ)

坚持坚持啦 ~ ᵎ(•̀㉨•́)و ̑̑ 


【本章目标】

1.面向过程和面向对象初步认识
2.类的引入
3.类的定义
4.类的访问限定符及封装
5.类的作用域
6.类的实例化
7.类的对象大小的计算
8.类成员函数的this指针


目录

【本章目标】

1.什么是面向过程?什么是面向对象?

2.类的引入

3.类的定义:

3.1定义类的两种方法:

(1)声明和定义全部放在类里面

(2)声明放在 .h 文件里面,类的定义放在 .cpp 文件里面

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

4.1访问限定符

4.2封装

5.类的作用域

6.类的实体化

7.类对象模型

7.1如何计算类对象的大小

7.2结构体内存对齐规则

8. this 指针

8.1  this 指针的引出

8.2  this 指针的特点

8.3看看下面程序编译的运行结果是什么?

1)理解一

2)理解二


1.什么是面向过程?什么是面向对象?

我们都知道:

  • C语言,是面向过程的,关注的是过程,分析出求解问题的每一步,通过函数调用来逐步解决
  • C++,是面向对象的,关注的是对象,讲一件事拆分为不同的对象,靠对象之间的交互完成

我们用一个具体的时间来理解,就比如洗衣服这件事

1)用面向过程的方法:(C语言)

  1. 打开洗衣机
  2. 放入衣服和洗衣液
  3. 洗衣机开始洗衣服
  4. 等待洗完,拿出衣服晾干

2)用面向对象的方法:(C++)

我能会讲这件事分为两个对象:人 和 洗衣机,在对每一个对象进行分析,它需要做哪些事情

人做三件事:

  1. 打开洗衣机
  2. 放入衣服和洗衣液
  3. 拿出衣服晾干

洗衣机只需要做一件事:洗衣服即可

总结:

我们能看出来,面向过程和面向对象是两种思考问题的方式,处理问题的角度不一样

面对过程的思维方式,更加关注事情的每一步。能更加高效、直接

面对对象的思维方式,更加关注事情的参与者、分类。对每一个类有不同的规划,这样在这件事的解决方面能更好的管理和维护

以上即时两种思维的区别

2.类的引入

C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数

比如: 之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现, 会发现struct中也可以定义函数。

struct Student    //class Struct 是C++中常用的使用方法
{
	void Init(const char* name, const char* gender, int age)
	{
		strcpy(_name, name);
		strcpy(_gender, gender);
		_age = age;
	}

	void Print()
	{
		cout << _name << " " << _gender << " " << _age << endl;
	}

	// 这里变量的声明并不是必须加_,可以根据自己的喜好或者之后公司的规定去加
	// 习惯加这个,用来标识成员变量
	char _name[20];
	char _gender[3];
	int _age;
};

int main()
{
	struct Student s1; // C语言的用法,需要加上struct
	Student s2; // C++中,可以直接用Student

	s1.Init("摸鱼", "外星人", 666);
	s1.Print();

	return 0;
}

在结构体的定义中,C++更习惯于用 class 来替代 struct 

3.类的定义:

语法:

class classname
{
	//类体:由成员变量和成员函数组成
	//...
};	//注意后面的分号

其中:

  • class  是定义类的关键字
  • classname  是类的名称
  • {  };   中的内容是主体,注意结束时的分号

类中的元素:称为类的成员

类中的变量:称为成员变量

类中的函数:称为成员函数

3.1定义类的两种方法:

(1)声明和定义全部放在类里面

需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。

class Data
{
	//类里的内容由成员函数和成员变量组成
public:
	Data(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    void Print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
private:
	int _year = 0;
	int _month = 0;
	int _day = 0;
};

(2)声明放在 .h 文件里面,类的定义放在 .cpp 文件里面

 我们一般都更希望,采用第二种方式来写

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

4.1访问限定符

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

访问限定符的说明:

  1. public  修饰的成员在类外可以直接被访问
  2. protected  和  private  修饰的成员在类外不能直接被访问
    (此处protectedprivate是类似的,他们的不用以后会讲)
  3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  4. 如果后面没有访问限定符,作用域就到 } 即类结束
  5. class的默认访问权限为private,struct为public(因为struct要兼容C)

注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

C++ 需要兼容 C 语言,所以 C++ 中 struct 可以当成结构体去使用。
另外 C++ 中 struct 还可以用来定义类,和 class 是定义类是一样的;
区别是 struct 的成员默认访问方式是 public,class 是的成员默认访问方式是 private;

4.2封装

什么是封装?

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

通过访问修饰符(如private)来修饰成员变量和成员方法,隐藏你不想让别人知道的代码,只提供接口让别人使用就行

封装的优点:

  • 隐藏代码细节,只提供公共访问
  • 提到代码复用性
  • 提高安全性

5.类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。

在类体外定义成员时,需要使用 : : 作用域操作符指明成员属于哪个类域。

class Student
{
public:
	//显示基本信息
	void Print();

private:
	char _name[20];
	char _gender[3];
	int _age;
};

//这里需要指定Print是属于Student这个类域,所以需要加上::作用域限定符
void Student::Print()
{
	cout << _name << " " << _gender << " " << _age << endl;
}

6.类的实体化

用类类型创建对象的过程,称为类的实例化

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

比如:入学时填写的学生信息表,表格就可以看成是一个类,来描述具体学生信息。

(2)一个类可以实例化出多个对象,实例化出的对象占用实际物理空间,存储成员变量

(3)打个比方:类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间

类就像当于一个图纸,根据图纸建造的房屋就是实例化出来的对象

只有实例化出来的对象栈用物理空间(也就是只有房子能住人,图纸不能)

 

7.类对象模型

7.1如何计算类对象的大小

//类中由成员函数和成员变量
class A1
{
public:
	void Print();
private:
	int a;
};

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

//类中仅有成员变量
class A3
{
private:
	int a;
};

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


int main()
{
	cout << sizeof(A1) << endl;

	cout << sizeof(A2) << endl;

	cout << sizeof(A3) << endl;

    cout << sizeof(A4) << endl;

	return 0;
}

如上代码,一个类中既可以有成员变量,又可以有成员函数,

那么一个类的对象中包含了什么?类的大小又是如何计算的呢?

可以看到仅有成员函数和空类都是一个字节,

结论:

  • 空类需要占用一个字节来唯一标识这个类的对象
  • 一个类的大小只取决于这个类的“成员变量”之和
  • 成员函数大小不计算在内
  • 注意计算过程中的结构体内存对其规则

这时候就有人会问了:为啥成员函数不占用类对象的大小嘞?

我们可以这样理解,类对象就是一个别墅用该类所有的对象就是一个别墅小区

每个对象里面的成员变量,就是每个别墅里面的私人财产,如私人的跑车,直升机之类的

成员函数就类似于小区里面的公共场所,如篮球场,超市之类的

类对象在用成员变量时,就是在用自己私有的东西,所以需要算进对象大小里面

而类对象在用成员函数的时候,就相当于去篮球场打球,去超市购物,用的是共有的东西,所以不算进对象大小里面

这个公共区域叫做:代码段

7.2结构体内存对齐规则

  1. 第一个成员在与结构体偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
    VS中默认的对齐数为8
  3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
    体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

8. this 指针

8.1  this 指针的引出

如下代码,我们定义一个日期类date

class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

int main()
{
	Date d1;
	d1.Init(2023, 1, 1);

	Date d2;
	d2.Init(2023, 3, 6);

	d1.Print();
	d2.Print();

	return 0;
}

结果如下:

 对于上述结果,大家有没有考虑过这样一个问题:

用 Date 类创建了 d1 和 d2 两个对象,并进行了初始化赋值,那么当我们调用 Print 函数去打印的时候,Print 函数是如何区分 d1 和 d2 对象的呢?

也就是他怎么知道d1是哪个,d2是哪个的?

这就要引出我们的新概念:this指针

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

 

 8.2  this 指针的特点

  1. this指针的类型:类的类型* const
    被 const 修饰以后,this 指针本身不能被修改,但是 this 指针指向的对象可以被修改,还可以进行初始化。
  2. this 指针只能在 “成员函数” 的内部使用。
     
  3. this 指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给 this 形参,所以对象中不存储 this 指针。
     
  4. this 指针是成员函数第一个隐含的指针形参,存放在栈里面,一般情况由编译器通过 ecx 寄存器自动传递,不需要用户传递。

8.3看看下面程序编译的运行结果是什么?

1)理解一

class A
{
public:
	void Show()
	{
		cout << "Show()" << endl;
	}
private:
	int aaa;
};

int main()
{
	A* p = nullptr;
	p->Show();
}

编译结果:

原因:

这里指针 p 是一个类对象的空指针,但当执行 p->Show() 时,程序并不会崩溃,而是正常打印。

因为对象里面存储的只有成员变量,像 Show() 这些成员函数的地址并没有存到对象里面,而是存在公共代码段的。所以这里能够正常运行

2)理解二

class A
{
public:
	void printA()
	{
		cout << _a << endl;
	}
private:
	int aaa;
};

int main()
{
    A* p = nullptr;
	p->printA();

	return 0;
}

编译结果: 

崩溃原因:

当执行  p->printA()   时,这里并不会产生错误,理由理解一中所讲

但是在  printA  函数内部需要打印成员变量aaa,成员变量  aaa 是空的,是访问不到的,所以会报错

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

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

相关文章

(1)QT基础铺垫

目录 1.Qt特性 2. 新建项目 3. 工作目录与构建目录 4. 工作目录 4.1 .pro 项目配置文件 4.2 dialog.h 4.3 dialog.cpp 4.4 main.cpp 5. 帮助文档 6. 调试信息 1.Qt特性 Qt经常被当作是一个基于c语言的gui开发框架&#xff0c;但是这并不是qt的全部&#xff0c;除了开…

JavaWeb( 二 ) URL

1.4.URL统一资源定位符 URL代表Uniform Resource Locator 统一资源定位符&#xff0c;也叫 URL地址 。是用于标识和定位Web上资源的地址&#xff0c;通常用于在Web浏览器中访问网站和文件。 URL由若干部分组成&#xff0c;scheme:// host : port / path 例如&#xff1a; htt…

WxGL应用实例:绘制点云

WxGL附带了几个工具函数&#xff0c;其中read_pcfile用来解析.ply和.pcd格式的点云文件&#xff0c;该函数返回一个PointCloudData类实例&#xff0c;包含以下属性&#xff1a; PointCloudData.ok - 数据是否可用&#xff0c;布尔型PointCloudData.info - 数据可用性说明&…

《通过十几轮数据进行模型训练,实现精确的无创血糖测量的演绎学习》阅读笔记

目录 0 演绎学习 1 论文摘要 2 论文十问 3 论文亮点与不足之处 4 与其他研究的比较 5 实际应用与影响 6 个人思考与启示 参考文献 0 演绎学习 在本文中&#xff0c;DL指的是Deduction Learning&#xff0c;即演绎学习方法。该方法是一种机器学习方法&#xff0c;通过使…

简单毛概刷题网页制作 3.0(拖欠近一年版)

原因是大概一年之前学校的毛概期末刷题网站突然崩了&#xff0c;但是一直没有修复。当时眼看着复习时间逐渐被压缩&#xff0c;自己啥也做不了&#xff0c;遂自学前端完成毛概刷题网页一枚。 最早的毛概刷题网站仅仅是 1.0 版本&#xff08;传送门&#xff09;&#xff0c;功能…

STM32F4_USMART调试组件

目录 1. USMART是什么&#xff1f; 2. USMART的特点 3. USMART实现流程 4. USMART组件 5. 在usmart_config.c中添加想要被USMART调用的函数 6. 实验程序 6.1 main.c 6.2 usmart.c 6.3 usmart.h 7. USMART调试的优越性说明 1. USMART是什么&#xff1f; USMART 是 AL…

org.apache.poi 设置 Excel 单元格颜色 RGB

一、背景说明 在使用 org.apache.poi 导出 Excel 时&#xff0c;需要设置部分单元格的颜色。 可以使用方法&#xff1a;org.apache.poi.ss.usermodel.CellStyle.setFillForegroundColor() 和 org.apache.poi.ss.usermodel.CellStyle.setFillPattern() 来设置单元格的颜色和填…

低频量化之 可转债 配债数据及策略 - 全网独家

目录 历史文章可转债配债数据 待发转债&#xff08;进展统计&#xff09;待发转债&#xff08;行业统计&#xff09;待发转债&#xff08;5证监会通过&#xff0c;PE排序&#xff09;待发转债&#xff08;5证监会通过&#xff0c;安全垫排序&#xff09;待发转债&#xff08;5证…

【算法】一文彻底搞懂ZAB算法

文章目录 什么是ZAB 算法&#xff1f;深入ZAB算法1. 消息广播两阶段提交ZAB消息广播过程 2. 崩溃恢复选举参数选举流程 ZAB算法需要解决的两大问题1. 已经被处理的消息不能丢2. 被丢弃的消息不能再次出现 最近需要设计一个分布式系统&#xff0c;需要一个中间件来存储共享的信息…

Java 怎样实现代理模式,有什么优缺点

一、介绍 代理模式是一种常见的设计模式&#xff0c;它可以为其他对象提供一种代理以控制对这个对象的访问。代理对象具有与被代理对象相同的接口&#xff0c;客户端无需知道代理对象和被代理对象的区别。代理模式可以应用于各种不同的场景&#xff0c;例如远程代理、虚拟代理…

SpringBoot整合Mybatis-plus实现多级评论

在本文中&#xff0c;我们将介绍如何使用SpringBoot整合Mybatis-plus实现多级评论功能。同时&#xff0c;本文还将提供数据库的设计和详细的后端代码&#xff0c;前端界面使用Vue2。 数据库设计 本文的多级评论功能将采用MySQL数据库实现&#xff0c;下面是数据库的设计&…

vcruntime140.dll无法继续执行代码?vcruntime140.dll如何修复?只需要3步即可

vcruntime140.dll是用于Microsoft Visual C Redistributable&#xff08;可再发行组件&#xff09;的一部分&#xff0c;它是一个动态链接库文件&#xff0c;包含了该软件包提供的运行库。在许多应用程序和游戏中&#xff0c;vcruntime140.dll文件经常被使用。如果该文件缺失或…

spark 数据的加载和保存(Parquet、JSON、CSV、MySql)

spark数据的加载和保存 SparkSQL 默认读取和保存的文件格式为 parquet 1.加载数据 spark.read.load 是加载数据的通用方法 scala> spark.read. csv format jdbc json load option options orc parquet schema table text textFile 如果读取不同格式的数据&#xff0c;可以…

后端要一次性返回我10万条数据

问题描述 面试官&#xff1a;后端一次性返回10万条数据给你&#xff0c;你如何处理&#xff1f;我&#xff1a;歪嘴一笑&#xff0c;what the f**k! 问题考察点 看似无厘头的问题&#xff0c;实际上考查候选人知识的广度和深度&#xff0c;虽然在工作中这种情况很少遇到... …

情景剧本杀闯关系统

情景剧本杀闯关软件的开发需求通常包括以下几个方面&#xff1a; 剧本设计&#xff1a;开发者需要根据用户需求和市场调研&#xff0c;设计不同主题和难度等级的剧本内容&#xff0c;以及游戏过程中的任务、角色和道具等。 游戏引擎开发&#xff1a;为了实现游戏过程中…

如何在 DigitalOcean 中部署 ONLYOFFICE 文档

现在您可使用通过 DigitalOcean 市场提供的一键式应用在 DigitalOcean 云架构中轻松部署 Docker 版本的 ONLYOFFICE 文档。 一键式应用是一个包含所有必要预配置组件的镜像&#xff0c;可用于便捷地在运行有 Ubuntu OS 的 DigitalOcean 服务器上部署 ONLYOFFICE&#xff1a; D…

Azure DevOps Server 2022.0.1升级手册

Contents 1. 概述2. 操作方法 2.1 安装操作系统2.2 安装数据库2.4 还原数据2.3 安装和配置Azure DevOps Server 1. 概述 Azure DevOps Server 是微软公司经过20多年的持续开发&#xff0c;逐渐将需求管理、敏捷实践、源代码管理、持续集成等功能集成一体&#xff0c;实现应用软…

B-Tree (多路查找树)分析-20230503

B-Tree (多路查找树)学习-20230503 前言 B-树是一类多路查询树&#xff0c;它主要用于文件系统和某些数据库的索引&#xff0c;如果采用二叉平衡树访问文件里面的数据&#xff0c;最坏情况下&#xff0c;磁头可能需要进行O(h)次对磁盘的读写&#xff0c;其中h为树的高度&…

微服务不是本地部署的最佳选择,不妨试试模块化单体

微服务仅适用于成熟产品 关于从头开始使用微服务&#xff0c;马丁・福勒&#xff08;Martin Fowler&#xff09;总结道&#xff1a; 1. 几乎所有成功的微服务都是从一个过于庞大而不得不拆分的单体应用开始的。 2. 几乎所有从头开始以微服务构建的系统&#xff0c;最后都会因…

Java 反射机制

目录 一、反射机制概述 二、理解并获取Class实例 三、反射的用法 1. 通过反射创建运行时类的对象 2. 通过反射获取运行时类的属性结构 3. 通过反射获取运行时类的方法结构 4. 通过反射获取运行时类的构造器结构 5. 通过反射获取运行时类的父类 6. 通过反射获取运行时类…