C++ 之 【类与对象】从入门到精通一条龙服务 入门篇

不要觉的自己很没用,其实你还可以给家人带来温暖,比如爸妈看到你就来火

目录:

一、面向过程和面向对象初步认识

二、类的引入

三、类的定义

四、类的访问限定符及封装

1.访问限定符

2.封装

五、类的作用域

六、类的实例化

七、类的对象大小的计算

1.类对象的存储方式猜测

2.结构体内存对齐规则

八、类成员函数的this指针

1.this指针的作用

2.this指针的特性

3.面试题练习

九、完结撒❀

前言:

对于类和对象的讲解一共划分为3篇,这是第1篇入门篇学习,更适合小白宝宝的体质

–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀-正文开始-❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–

一、面向过程和面向对象初步认识

C 语言是 面向过程 的, 关注 的是 过程 ,分析出求解问题的步骤,通过函数调用逐步解决问题。
比如我们可以拿洗衣服为例:

C++ 基于面向对象 的, 关注 的是 对象 ,将一件事情拆分成不同的对象,靠对象之间的交互完
成。

二、类的引入

C 语言结构体中只能定义变量,在 C++ 中,结构体内不仅可以定义变量,也可以定义函数。 比如:
之前在数据结构初阶中,用 C 语言方式实现的栈,结构体中只能定义变量 ;现在以 C++ 方式实现,
会发现 struct 中也可以定义函数。
举例代码如下:
typedef int DataType;
struct Stack
{void Init(size_t capacity){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (nullptr == _array){perror("malloc申请空间失败");return;}_capacity = capacity;_size = 0;}void Push(const DataType& data){// 扩容判断 此处省略_array[_size] = data;++_size;}DataType Top(){return _array[_size - 1];}void Destroy(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}DataType* _array;size_t _capacity;size_t _size;
};int main()
{Stack s;s.Init(10);s.Push(1);s.Push(2);s.Push(3);cout << s.Top() << endl;s.Destroy();return 0;
}
 

可以看到在上面代码中,结构体stack里面定义了变量:

DataType * _array ;
size_t _capacity ;
size_t _size ;
还定义了函数:
void Init ( size_t capacity )
void Push ( const DataType & data )
DataType Top ()
void Destroy ()
所以在使用这些函数时,直接在创建的结构体变量中调用即可完成。
上面结 构体的定义, C++ 中更喜欢用 class 来代替,也就是struct换成class。

三、类的定义

类的定义如下:
 

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

类的两种定义方式:
1. 声明和定义全部放在类体中,需注意:成员函数如果 在类中定义 ,编译器可能会将其当成
联函数 处理。

2. 类声明放在 .h 文件中,成员函数定义放在 .cpp 文件中,注意: 成员函数名前需要加类名 ::

在后序工作中一般使用第二种定义方式

在上面演示图片中的代码,细心的同学可能已经注意到了,我们定义人的结构体变量名前面都加上了一个_,其实这是为了防止变量名冲突混淆含义而定义的

举例如下:
 

class Data
{
public://我们看这个函数会感觉很僵硬void Init(int year, int month, int day){//这里的year、month、day到底是成员变量还是函数形参year = year;month = month;day = day;}private:int year;int month;int day;
};

上面代码无法区分Init函数中谁是形参谁是变量

所以我们在实际中一般这样写:
 

class Data
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};

其他命名方式都可,这主要看公司要求,一般都是加一个前缀加一个后缀标识区分就行。

上面定义结构体中我们使用了public和private,这是类的访问限定符,下面我们对其进行间接。

四、类的访问限定符及封装

1.访问限定符

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

【访问限定符说明】
1. public 修饰的成员在类外可以直接被访问
2. protected private 修饰的成员在类外不能直接被访问 ( 此处 protected private 是类似的 )
3. 访问权限 作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. 如果后面没有访问限定符,作用域就到 } 即类结束。
5. class的默认访问权限为privatestructpublic(因为struct要兼容 C)
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
我们还拿上面代码举例讲解:
class Data
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};

上面代码,类中共有域为public到private的范围,私有域为private到类结束为止

那么我们在主函数中创建类变量即可访问到函数Init,但是访问不到类的成员变量,因为成员变量范围在私有域内,所以只能在类里面才能被访问

class Data
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};int main()
{Data da1;da1.Init(2024, 4, 7);//直接访问da1.Init(_day);//直接报错return 0;
}

之前看到的一道【面试题】:

问题: C++ struct class 的区别是什么?
解答: C++ 需要兼容 C 语言,所以 C++ struct 可以当成结构体使用。另外 C++ struct 还可以用来
定义类。和 class 定义类是一样的,区别是 struct 定义的类默认访问权限是 public class 定义的类
默认访问权限是 private
注意:在继承和模板参数列表位置, struct class 也有区别,这里对于才学习了本章节知识的同学们不太方便进行说明,之后在我下面的博客会继续进行讲解

2.封装

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来
和对象进行交互。
封装本质上是一种管理,让用户更方便使用类
比如: 对于电脑这样一个复杂的设备,提供给用 户的就只有开关机键、通过键盘输入,显示器, USB 插孔等,让用户和计算机进行交互,完成日 常事务。但实际上电脑真正工作的却是 CPU 、显卡、内存等一些硬件元件。

对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的, CPU 内部是如
何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此
算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以
及键盘插孔等,让用户可以与计算机进行交互即可
C++ 语言中实现封装,可以 通过类将数据以及操作数据的方法进行有机结合,通过访问权限来
隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用

五、类的作用域

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

比如下面代码:
 

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

六、类的实例化

用类类型创建对象的过程,称为类的实例化
1. 类是对对象进行描述的 ,是一个 模型 一样的东西,限定了类有哪些成员,定义出一个类 并没
有分配实际的内存空间 来存储它;比如:入学时填写的学生信息表,表格就可以看成是一个
类,来描述具体学生信息
2. 一个类可以实例化出多个对象, 实例化出的对象 占用实际的物理空间,存储类成员变量

还拿上面代码举例:
 

class Data
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};int main()
{Data da1;da1.Init(2024, 4, 7);//直接访问da1.Init(_day);//直接报错return 0;
}

类中的成员变量_year,_month,_day都是对其进行的声明,并没有开辟真正的空间,只有当定义出类变量da1时才会真正对类中的成员变量开辟空间,这就是类的实例化。

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

七、类的对象大小的计算

类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算
一个类的大小?
我们举例猜测下面代码的存储方式:
 
class Person
{
public:void PrintPersonInfo();void SetPersonInfo();private:char _name[20];char _gender[3];int  _age;
};

1.类对象的存储方式猜测

··· 对象中包含类的各个成员

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

··· 代码只保存一份,在对象中保存存放代码的地址

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

问题:对于上述三种存储方式,那计算机到底是按照那种方式来存储的?

我们再通过对下面的不同对象分别获取大小来分析看下

class A1 {
public:void f1(){}
private:int _a;
};// 类中仅有成员函数
class A2 {
public:void f2() {}
};// 类中什么都没有---空类
class A3
{};

分别计算每个类的大小可以直到

sizeof(A1):4          sizeof(A2) : 1          sizeof(A3) : 1

A1中有一个成员函数和成员变量构成,总大小为4个字节

A2只有一个成员函数组成,总大小为1个字节

A3为空类,里面什么都没定义,总大小为1个字节

空类和只有一个成员函数的类大小相等都为1个字节,说明类的空间大小并没有计算成员函数的大小

那么我们就可以推测出

结论:一个类的大小,实际就是该类中 成员变量 之和,当然要注意内存对齐, 注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。

2.结构体内存对齐规则

下面我们再回顾学习一下结构体的内存对齐规则

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

八、类成员函数的this指针

1.this指针的作用

我们先看一下下面代码:
 

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 Print 两个成员函数,函数体中没有关于不同对象的区分,那当 d1 调用 Init
数时,该函数是如何知道应该设置 d1 对象,而不是设置 d2 对象呢?
C++ 中通过引入 this 指针解决该问题,即: C++ 编译器给每个 非静态的成员函数 增加了一个隐藏
的指针参数,让该指针指向当前对象 ( 函数运行时调用该函数的对象 ) ,在函数体中所有 成员变量
的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编
译器自动完成
因为this是编译器自动完成的,不需要用户自己完成,所以 上面代码实际运行实现如下
class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}//void Print()void Print(Data* const this){//cout << _year << "-" << _month << "-" << _day << endl;cout << this->_year << "-" << this->_month << "-" << this->_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;
}

上面代码中this指针所在位置就是编译器自己进行处理的操作,因为是隐含的this指针,形参和实参的位置不能显示写,编译器会自行处理,但是this指针是可以在类里面显示着进行使用的

我们需要知道的是函数体是进行对象区分是通过this指针自动将成员变量的地址传递了过去,当作函数的形参进行了使用。

2.this指针的特性

1. this 指针的类型:类类型 * const ,即成员函数中,不能给 this 指针赋值。
2. 只能在 成员函数 的内部使用
3. this 指针本质上是 成员函数 的形参 ,当对象调用成员函数时,将对象地址作为实参传递给
this 形参。所以 对象中不存储 this 指针
4. this 指针是 成员函数 第一个隐含的指针形参,一般情况由编译器通过 ecx 寄存器自动传
递,不需要用户传递

3.面试题练习

下面我们来看两道【面试题】

问:this 指针存在哪里?
答:一般情况下this指针作为形参会存放于栈区当中,有时根据编译器的不同会存放在寄存器ecx当中
根据下面代码选择ABC中一个选项:
// 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、正常运行

题解:定义出的类指针变量赋值为空指针,之后访问Print成员函数,这里我们就要清楚程序是如何进行Print函数的访问的——通过this指针,而我们只是向Print函数传了一个空指针作为形参而已,并且我们并没有对空指针进行访问,所以不会出现报错,所以程序最终进入Print函数打印出了Printf()字符串。

根据下面代码选择ABC中一个选项:

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{ 
public:void PrintA() {cout<<_a<<endl;}
private:int _a;
};int main()
{A* p = nullptr;p->PrintA();return 0;
}

答案:B

题解:上面代码中PrintA函数里cout<<_a<<endl;在编译运行时真正的运行的代码为:cout<<this->_a<<endl;而这里的this指针为空指针,对空指针访问成员变量最后程序就会运行崩溃。

九、完结撒❀

如果以上内容对你有帮助不妨点赞支持一下,以后还会分享更多编程知识,我们一起进步。
最后我想讲的是,据说点赞的都能找到漂亮女朋友❤

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu 22.04安装新硬盘并启动时自动挂载

方法一 要在Ubuntu 22.04系统中安装一个新硬盘、对其进行格式化并实现启动时自动挂载&#xff0c;需要按以下步骤操作&#xff1a; 1. 安装硬盘 - 确保你的硬盘正确连接到计算机上&#xff08;涉及硬件安装&#xff09;。 2. 发现新硬盘 - 在系统启动后&#xff0c;打开终端…

IntelliJ IDEA 2024.1 更新亮点汇总:全面提升开发体验

IntelliJ IDEA 2024.1 更新亮点汇总&#xff1a;全面提升开发体验 文章目录 IntelliJ IDEA 2024.1 更新亮点汇总&#xff1a;全面提升开发体验摘要引言 IntelliJ IDEA 2024.1 的新增功能主要亮点全行代码完成 最终的支持 Java 22 功能新航站楼 贝塔编辑器中的粘滞线 人工智能助…

【数字图像处理matlab系列】空间域处理之亮度变换(imadjust函数使用)

【数字图像处理matlab系列】空间域处理之亮度变换(imadjust函数使用) 在空间域中&#xff0c;图像处理就是直接对图像的像素进行操作 imadjust 是 MATLAB 中用于调整图像强度值或颜色图的函数。它可以改变图像的对比度&#xff0c;使得图像更清晰或更易于分析。以下是 imadju…

从零到部署指南:Ubuntu上安装Boost和Crow库

1.安装boost 在安装Crow之前&#xff0c;需要确保您的系统中已经安装了Boost库。以下是Boost库安装步骤&#xff1a; 首先&#xff0c;从Boost官方网站或通过特定的链接下载Boost的源码&#xff0c;boost源码具体可参看这个链接&#xff1a; https://blog.csdn.net/duan199201…

LangChain-13 Memory 通过 ConversationBufferMemory 记录会话的上下文 并在对话中自动检索

问题背景 平常我们的任务都是一次性会话&#xff0c;大模型是不会记录你的上下文的。如果我们要进行持续会话&#xff0c;或者希望大模型有记忆的话&#xff0c;我们需要对上下文进行管理。但这个过程是比较复杂的&#xff0c;LangChain 提供了一系列的工具来帮助我们简化过程…

golang web 开发 —— gin 框架 (gorm 链接 mysql)

目录 1. 介绍 2. 环境 3. gin 3.1 gin提供的常见路由 3.2 gin的分组 main.go router.go 代码结构 3.3 gin 提供的Json方法 main.go route.go common.go user.go order.go 3.4 gin框架下如何获取传递来的参数 第一种是GET请求后面直接 /拼上传递的参数 第二种是…

Mysql启动报错:本地计算机上的mysql服务启动后停止,某些服务在未由其他服务或程序使用时将自动停止

Mysql启动报错&#xff1a;本地计算机上的mysql服务启动后停止,某些服务在未由其他服务或程序使用时将自动停止 文章目录 Mysql启动报错&#xff1a;本地计算机上的mysql服务启动后停止,某些服务在未由其他服务或程序使用时将自动停止1. 备份mysql的data文件夹2. 重新构建 Wind…

亮数据----教你轻松获取数据

文章目录 1. 数据采集遇到的瓶颈1.1 不会造数据&#xff1f;1.2 不会写爬虫代码&#xff1f; 2.IP 代理基础知识2.1 基本概念2.2 作用和分类2.3 IP 代理的配置和使用2.4 安全和合规 3. 为何使用亮数据 IP 代理3.1 拥有丰富的代理网络服务3.2 简单易操作的采集工具3.3 拥有各平台…

QT软件开发: 点击鼠标在窗口里绘制矩形(窗口透明背景)

QT软件开发: 点击鼠标在窗口里绘制矩形(窗口透明背景)-腾讯云开发者社区-腾讯云 一、功能需求 一般在软件开发中&#xff0c;需要都有选择区域的需求&#xff0c;比如&#xff1a; 1. 截图软件&#xff0c;需要鼠标选择指定区域截图 2. 屏幕录像软件&#xff0c;需要鼠标选…

《C语言深度解剖》(2):详解C语言分支语句和循环

&#x1f921;博客主页&#xff1a;醉竺 &#x1f970;本文专栏&#xff1a;《C语言深度解剖》 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多数据结构与算法点击专栏链接查看&am…

基于wsl的Ubuntu20.04上安装桌面环境

在子系统Ubuntu20.04上安装桌面环境 1. 更换软件源 由于Ubuntu默认的软件源在国外&#xff0c;有时候后可能会造成下载软件卡顿&#xff0c;这里我们更换为国内的阿里云源&#xff0c;其他国内源亦可。 双击打开Ubuntu20.04 LTS图标&#xff0c;在命令行中输入 # 备份原来的软…

ThinkPHP审计(2) Thinkphp反序列化链5.1.X原理分析从0编写POC

ThinkPHP审计(2) Thinkphp反序列化链子5.1.X原理分析&从0编写POC 文章目录 ThinkPHP审计(2) Thinkphp反序列化链子5.1.X原理分析&从0编写POC动态调试环境配置Thinkphp反序列化链5.1.X原理分析一.实现任意文件删除二.实现任意命令执行真正的难点 Thinkphp反序列化链5.1.…