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

news2024/11/15 8:06:06

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 是空的,是访问不到的,所以会报错

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

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

相关文章

情景剧本杀闯关系统

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

TensoRT量化第四课:PTQ与QAT

目录 PTQ与QAT前言1. TensorRT量化2. PTQ3. QAT4. QAT实战4.1 环境配置4.2 pytorch_quantization简单示例4.3 自动插入QDQ节点 总结 PTQ与QAT 前言 手写AI推出的全新TensorRT模型量化课程&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考。 本次课程为第四课&am…

Netty基础(一)

1.概述 1.1.原生NIO存在的问题 1>.NIO的类库和API繁杂,使用麻烦: 需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等; 2>.需要具备其他的额外技能: 要熟悉Java多线程编程,因为NIO编程涉及到Reactor模式,你必须对多线程和网络编程非常熟悉,才能…

【数据结构与算法】图——邻接表与邻接矩阵

文章目录 一、图的基本概念二、图的存储结构2.1 邻接矩阵2.2 邻接表2.3 邻接矩阵的实现2.4 邻接表的实现 三、总结 一、图的基本概念 图&#xff08;Graph&#xff09;是由顶点的有穷非空集合和顶点之间边的集合组成&#xff0c;通常表示为&#xff1a;G&#xff08;V,E&#…

【服务器数据恢复】多块磁盘离线导致RAID5崩溃的数据恢复案例

服务器数据恢复环境&故障&#xff1a; 某品牌StorageWorks存储设备&#xff0c;8块磁盘组建一组raid5磁盘阵列。存储中2块磁盘掉线导致阵列崩溃&#xff0c;经过检查发现掉线的2块磁盘均存在物理故障。 服务器数据恢复过程&#xff1a; 1、硬件工程师对掉线的两块磁盘进行…

性能测试-压力测试如何快速上手?8年资深测试总结整理,永不背锅...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 一般我们在刚介入…

mulesoft MCIA 破釜沉舟备考 2023.05.04.30(易错题)

mulesoft MCIA 破釜沉舟备考 2023.05.04.30(易错题) 1. According to MuleSoft, which major benefit does a Center for Enablement (C4E) provide for an enterprise and its lines of business?2. An organization is choosing between API-led connectivity and other i…

ASEMI代理ADM3251EARWZ-REEL原装ADI车规级ADM3251EARWZ-REEL

编辑&#xff1a;ll ASEMI代理ADM3251EARWZ-REEL原装ADI车规级ADM3251EARWZ-REEL 型号&#xff1a;ADM3251EARWZ-REEL 品牌&#xff1a;ADI/亚德诺 封装&#xff1a;SOIC-20-300mil 批号&#xff1a;2023 引脚数量&#xff1a;20 工作温度&#xff1a;-40C~85C 安装类型…

vim常用命令总结

vim常用命令总结 &#xff08;转) 在命令状态下对当前行用 &#xff08;连按两次&#xff09;, 或对多行用n&#xff08;n是自然数&#xff09;表示自动缩进从当前行起的下面n行。你可以试试把代码缩进任意打乱再用n排版&#xff0c;相当于一般IDE里的code format。使用ggG可对…

如何在Windows AD域中驻留ACL后门

前言 当拿下域控权限时&#xff0c;为了维持权限&#xff0c;常常需要驻留一些后门&#xff0c;从而达到长期控制的目的。Windows AD域后门五花八门&#xff0c;除了常规的的添加隐藏用户、启动项、计划任务、抓取登录时的密码&#xff0c;还有一些基于ACL的后门。 ACL介绍 …

X3派 部署pytorch yolov5 demo

一、配置环境 我的pytorch之前已配置了&#xff0c;参照链接&#xff1a; 安装anconda配置pytorch 查看环境conda env list 激活环境 conda activate yolov5_py3.10 安装onnx&#xff1a;pip install -i https://pypi.tuna.tsinghua.edu.cn/simple onnx 安装yolov5需要的包…

小程序中使用CANVAS实现手写签名并写入模板图片中

实测&#xff0c;开发者工具中滚动条位置会影响书写&#xff0c;显示会有些问题&#xff0c;手机上测试正常 index.js const App getApp();Page({/*** 页面的初始数据*/data: {curScrollTop : 0},/*** 生命周期函数--监听页面加载*/onLoad(options) {},/*** 生命周期函数--监…

【软考高项笔记】第2章 信息技术发展2.1 信息技术及其发展

2.1 信息技术及其发展 获取信息、处理信息、传输信息、使用信息硬技术&#xff08;物化技术&#xff09;传感器&#xff0c;服务器&#xff0c;手机&#xff0c;软技术&#xff08;非物化&#xff09;数据分析&#xff0c;规划决策2.1.1 计算机软硬件 硬件 物理装置 &#xff…

知识管理在企业中的重要性

随着经济全球化和信息化的快速发展&#xff0c;企业面临着越来越多的竞争和挑战。如何把握市场动态、满足客户需求、提高产品质量和效率等&#xff0c;成为了企业发展中亟待解决的问题。而知识管理作为一种新兴的管理方式&#xff0c;逐渐引起了企业们的重视。本文将从以下几个…

go-zero

目录 引入开发派系标准库/自研派系——不要让框架束缚开发web框架派系——gingrpc大一统框架 go-zerogo-zero快速实现一个微服务user serviceorder api server启动 goctl安装生成的api网关目录生成的pb目录api语法syntaximport语法块infotypeservice注释 命令大全 引入 该图片来…

图论专题(各类算法和蓝桥杯真题例题)

1.图论入门 1.1存边方式 1.1.1 数组存边 1.1.2 临接矩阵存边 1.1.3 临接表存边 1.2 图的遍历和连通性 通过DFS和BFS遍历每一个图 对于非连通图&#xff0c;循环对每一个点dfs操作 也可以通过并查集来判断连通性 1.2.1全球变暖例题 import sys sys.setrecursionlimit(6000…

【目标检测】Haar-like特征检测简介

文章目录 一、背景模板匹配&#xff08;template matching&#xff09;关键点检测角点检测 二、harris特征提取原理Harris Detector 的具体流程&#xff1a;harris特征的可复用性旋转尺度 scale亮度 illuminationview point 三、Viola Jones检测原理Harr-like特征提取积分图训练…

QT教程demo之串口助手代码设计实现

关注WeChat Official Account 南山府嵌入式获取更多精彩 我创建了一个群关注V号后加入。因为这里不允许添加二维码 代码&#xff1a;QT_Pr 1-QT开发串口助手需要的基本文件 在QT6开发串口助手时&#xff0c;通常需要以下头文件&#xff1a; #include <QSerialPort> #i…

TCP协议策略

TCP可靠性 基于序号的确认应答(ACK)机制 TCP保证可靠性最核心的机制就是基于序号的确认应答机制。 TCP并不是百分之百可靠的&#xff0c;但是只要一条消息有应答&#xff0c;那么我们就可以确定该消息100%被对方收到了&#xff0c;这就是确认应答的意义。 可靠性不仅仅是保…

MOSS模型量化版部署过程

文章目录 项目背景配置环境与准备部署推理命令行部署报错1报错2&#xff1a; 网页版部署 项目背景 2023年4月21日&#xff0c;复旦大学自然语言处理实验室正式开放MOSS模型&#xff0c;是国内首个插件增强的开源对话大语言模型。MOSS 相关代码、数据、模型参数已在 GitHub 和 …