21天学会C++:Day9----初识类与对象

· CSDN的uu们,大家好。这里是C++入门的第九讲。
· 座右铭:前路坎坷,披荆斩棘,扶摇直上。
· 博客主页: @姬如祎
· 收录专栏:C++专题

目录

 1. 面向过程与面向对象

2. 类的定义

3. 类中的访问限定符

3.1 访问限定符的作用

4. 对象的实例化以及对象的大小

5. this指针

5.1 this指针的引入

5.2 VS中this指针的优化

5.3 考验你对this指针理解的两道题


 1. 面向过程与面向对象

我们之前学习的C语言是面向过程的语言,而我们正在学习的C++是面向对象的语言(OOP语言)。之前提到过,C++是对C语言的补充和改进。那么为什么C++要引入面向对象的概念呢?面向对象又比面向过程好在哪里呢?

面向过程,关注的是解决问题的步骤,将每一个功能都抽象出一个个具体的函数,然后逐步解决问题。

例如:我们在家洗衣服,站在面向过程的角度来看是这个样子:

通过一个一个的步骤来解决问题。在编程中就是通过一个一个的函数来实现。

面向对象:在解决问题的过程中,面向对象的思想是将解决该问题中的事物看作一个一个的对象,对象之间各司其职,达到解决问题的目的。

例如:在洗衣的过程中,人这个对象只需要将衣服放进洗衣机,等洗衣机洗好之后取出来即可。不需要关注洗衣机是怎么洗衣服的。 

现在来分析面向对象与面向过程的优缺点还是太早了。我们只要有一个大致印象,面向对象比面向过程更加高级,面向对象能够更加方便的将问题模块化, 更好的解决问题。但是相比于面向对象,面向过程的代码执行效率较高。

2. 类的定义

我们知道C++是兼容C语言的,C语言的结构体里面是不能定义函数的。但是C++的结构体里面是可以定义函数的,因为C++将结构体升级成为了类,像这样:

struct A
{int _a;void func(){cout << "func" << endl;}
};int main()
{struct A a1;return 0;
}

C++将结构体升级成为了类,那么结构体的名字就是类型的名字,因此在C++中定义结构体是不需要加上struct的。

struct A
{int _a;void func(){cout << "func" << endl;}
};int main()
{struct A a1;A a1; //c++不用加structreturn 0;
}

在C++里面更喜欢用class代替struct来定义一个类,于是我们顺理成章地推导出了class定义类的方法:

class 类名

{

        // 类的主体,包括成员变量和成员函数

};

注意:分号不能少。

// 定义一个类
class B
{int _b;void func(){cout << "func" << endl;}
};

上面的代码我们定义了一个类 B。 类中的变量(_b),叫做成员变量,类中定义的函数(func) 叫做成员函数 或者方法

C++规定:在类中的定义的函数,会被自动地视为inline函数,但是他最后是不是内联函数,还是取决于编译器。

注意:是在类中定义的函数,如果你是在类内声明,类外定义,不会被视为内联函数。

struct B
{int _b;void func() //函数的声明定义均在类里面{int a = 10;}void func(int a);
};void B::func(int a)
{int b = 10;
}int main()
{B b;b.func();b.func(1);return 0;
}

上面的代码,func() 函数就是在类内定义的函数, 而func(int a) 则是在类外定义的函数,通过调试观察汇编代码,我们可以看到func()已经是一个内联函数了。

关于内联函数的细节:21天学会C++:Day6----内联函数_姬如祎的博客-CSDN博客

这里还有一个要注意的点:类中函数类外定义的写法,需要加上类名和域作用限定符,告诉编译器这是这个类里面的函数的实现。不然可能会与全局域的函数冲突。

 

 class中的所有成员变量都在其所在的那个类域里面,这样做是理所应当的。

3. 类中的访问限定符

我们用struct定义了一个类 A,用class定义了一个类 B,创建一个变量之后。访问其各自的成员变量。发现struct定义的类可以直接访问,但是class定义的类不能直接访问成员变量。这是为啥呢?

struct A
{int _a;void func(){cout << "func" << endl;}
};class B
{int _b;void func(){cout << "func" << endl;}
};int main()
{A a;a._a = 10;B b;b._b = 10;return 0;
}

 这是因为C++中每一个类中的成员都会受到访问限定符的限制。我们来看看C++中的访问限定符有哪些:

其中public表示类成员在类内类外都可以访问;protected,private均是类成员在类内可以访问,在类外不可以访问。protected 与 private之间的区别需要我们学到继承的时候再讲。

于是我们就可以得出结论,在没有写访问限定符的时候,struct定义的类默认访问限定符是public;class定义的类默认访问限定符是private。

访问权限的作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。没有下一个访问限定符就是到类的结束位置。

3.1 访问限定符的作用

这里需要uu们回忆一下我们在使用C语言实现的数据结构,无论哪一个都行。我们就拿栈来说吧,我们当时定义的栈是这样的:

C语言数据结构初阶(5)----栈_姬如祎的博客-CSDN博客


//栈的数据类型
typedef int STDataType;
//栈的结构体,类比顺序表
typedef struct Stack
{STDataType* a; //栈的顺序存储的数组int top; //栈的top元素int capacity; //数组的容量
} ST;

这其中有一个top变量,我们在在实现栈的时候,提到过top可以指向栈顶元素或者栈顶元素的下一个位置,这取决于设计者的实现方式。

因为C语言结构体中的数据是公开的,于是,就会有程序员在访问栈顶的元素时写出这样的代码:

int main
{ST s;s.a[top];
}

是的,他不调用你实现的访问栈顶元素的函数,而是直接通过你的底层,直接访问数据。如果恰巧你实现的栈关于top的定义是实现方式2,碰巧他还是一个脾气暴躁的程序员,当他看到访问top的时候出现了随机值,他可能会直接破口大骂,这是谁写的 laji 代码。C语言没有常见数据结构的库也有一部分原因是这个吧。

没有访问限定符的限制,用户可以直接访问并修改任意数据,造成意料之外的结果,甚至导致程序崩溃。而有了访问限定符就能很好的解决这些问题。

我们只需要将stack的底层数据用private修饰就能很好的解决这些问题。同时将对应操作的函数用public修饰,提供对外接口供用户使用。


class Stack
{
public:void StackInit(){//}void StackPush(int val){//}//等等private:int* _a;int _capacity;int top;
};

这就是封装的具体表现了。

封装是面向对象的三大特性之一,另外两个是继承和多态。继承和多态后面讲解。

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。 封装本质上是一种管理,让用户更方便使用类。

4. 对象的实例化以及对象的大小

思考这样一个问题:你定义好了一个类,他是否在内存中占有空间呢?

答案显然是否定的,因为类是对象的描述,类在定义好时并不分配内存空间,只有在类实例化出对象之后,这个对象占有空间。我们完全可以把类当作一张图纸,而对象就是根据图纸生产出来的商品。

class A
{
public:void func(){cout << "func" << endl;}private:int _a;int* _p;
};int main()
{A a;return 0;
}

 那么我们应该如何计算对象的大小呢?拿上面的代码来说,sizeof(a)结果是什么呢?

(这里写sizeof(A) 也行,根据对象能算出大小,根据类肯定也行撒,类比图纸与商品)

在32位机器下,结果是8。对象大小的计算方式和结构体大小的计算是一样的,都遵循内存对齐。

但是,你可能会问,成员函数存在哪里呢?对象里面没有成员函数是怎么调用的呢?

在回答这些问题之前,我们先来思考。不同的对象调用类中的函数是调用的同一个吗?没错调用的就是同一个。

既然所有的对象都会调用相同的成员函数,那么为什么还要浪费空间在每个对象里面存一份函数的地址呢,因此类的成员函数是存在公共代码段的,源文件编译的时候编译器会找到函数的地址,我们不必关心。

既然对象里面只存储成员变量,那要是我定义的类里面没有成员变量,那他还会有空间吗?

我们看到结果是1,如果没有成员变量就没有空间的话,我们应该用什么来表征这个对象的存在呢?因此即使没有成员变量的类,实例化出的对象也是有空间的,至于具体的大小,依编译器而定。 

5. this指针

5.1 this指针的引入

我们创建了一个Date 类,类的成员变量用来存储年月日。InitiDate函数用来初始化一个Date对象的日期,ShowDate用来打印Date对象表示的日期。

class Date
{
public:void InitDate(int year, int month, int day){_year = year;_month = month;_day = day;}void ShowDate(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;};int main()
{Date d1;d1.InitDate(2004, 01, 01);d1.ShowDate();Date d2;d2.InitDate(2008, 01, 01);d2.ShowDate();
}

我们上一个小节讲到了一个类实例化出来一个对象就会开辟一份属于自己空间,那么上述代码中的d1,d2是两个不同的对象,也就各自拥有一份空间来存储各自表示的日期。但是类的成员函数只有一份,他是怎么做到不同的对象调用同一个函数时,找到不同空间中的数据(d1调用ShowDate打印的是d1中存储的信息,d2调用ShowDate打印的是d2中存储的信息)的呢?

这就得讲讲我们的this指针了,C++语法规定,对于非静态成员函数(没有加static修饰的成员函数),编译器会对成员函数做处理:在普通成员函数中加了一个隐藏的指针参数,让这个指针指向当前对象( 正在调用这个函数的对象,d1调用ShowDate,this指向的就是d1这个对象)。需要注意的是this指针不能在形参和实参显示传递,但可以在函数内部显示使用。

例如,ShowDate函数可以这样写:

void ShowDate()
{cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
}

现在我们就能理解为什么同一个函数能访问不同的空间了吧,因为隐藏传递了一个this指针,打印_year等变量,实际上是通过传递过来的this指针找到调用该函数的对象中的数据。因此才能做到一个函数访问两块空间。

那这里我就要问一个问题了,各位uu觉得this指针存储在哪里呢?

A:对象    B:栈    C:堆

答案:B。

A:如果this指针存在对象中,那么我们刚才计算对象大小的时候并没有计算this指针哇,因此排除a。

我们再来看看this的定义嘛,this是对象这个实参传递过来的,那么this就是形参撒,uu们形参当然是存在栈中的撒。因此this就是存在栈中的!!!

5.2 VS中this指针的优化

我们来看看VS对this指针的优化:我们通过反汇编来看看this指针的传递

我们可以看到,VS中直接将this指针存在了寄存器中,我们都知道寄存器的读写速度是非常快的,在类的内部,我们需要大量访问成员变量。而访问成员变量本质上是通过this指针来访问的。因此将他存储到ecx寄存器中能够提高访问的效率。 

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

我们看到这两道题都是通过一个空对象去调用函数,区别是在函数中是否访问对象中的成员变量。

答案:1:运行正常

           2:运行崩溃

在调用类成员函数的时候,会隐士传递this指针,都传递的是一个空指针,题目一并没有对this指针解引用,但是题目二确尝试解引用当问成员变量。因为操作成员变量的本质都是通过this指针,因此题目二会发生空指针的解引用,引起程序崩溃。

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

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

相关文章

基于Java的新能源充电系统的设计与实现(亮点:完整合理的充电流程,举报反馈机制、余额充值、在线支付、在线聊天)

新能源充电系统 一、前言二、我的优势2.1 自己的网站2.2 自己的小程序&#xff08;小蔡coding&#xff09;2.3 有保障的售后2.4 福利 三、开发环境与技术3.1 MySQL数据库3.2 Vue前端技术3.3 Spring Boot框架3.4 微信小程序 四、功能设计4.1 主要功能描述 五、系统实现5.1 完整充…

xss渗透(跨站脚本攻击)

一、什么是XSS? XSS全称是Cross Site Scripting即跨站脚本&#xff0c;当目标网站目标用户浏览器渲染HTML文档的过程中&#xff0c;出现了不被预期的脚本指令并执行时&#xff0c;XSS就发生了。 这里我们主要注意四点&#xff1a; 1、目标网站目标用户&#xff1b; 2、浏览…

Linux:centos9的本地yum仓库配置

其实9和7的配置方法是差不多一样的&#xff0c;只不过你使用7的本地yum仓库里面直接挂载就可以直接把仓库位置指向挂载点 具体可以看我往期文章&#xff0c;但是先看完我下面的描述再去看我链接的文章才能看懂如何配置centos9的yum仓库 Linux&#xff1a;YUM仓库服务_鲍海超-…

C#实现钉钉自定义机器人发送群消息帮助类

一、自定义机器人发送群消息使用场景 在企业中,针对一些关键指标内容(如每天的生产产量、每天的设备报警信息等信息),需要同时给多人分享,此时就可以将需要查看这些数据的人员都拉到一个群中,让群里的机器人将这些关键指标内容推送到群里即可【(目前已实现在钉钉群里创建…

STM32 学习笔记1:STM32简介

1 概述 STM32&#xff0c;从字面上来理解&#xff0c;ST 是意法半导体&#xff0c;M 是 Microelectronics 的缩写&#xff0c;32 表示 32 位&#xff0c;合起来理解&#xff0c;STM32 就是 ST 公司开发的 32 位微控制器。是一款基于 ARM 公司推出的基于 ARMv7 架构的 32 位 Co…

elasticsearch16-聚合API

个人名片&#xff1a; 博主&#xff1a;酒徒ᝰ. 个人简介&#xff1a;沉醉在酒中&#xff0c;借着一股酒劲&#xff0c;去拼搏一个未来。 本篇励志&#xff1a;三人行&#xff0c;必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》&#xff0c;SpringCloud…

unity学习第1天

本身也具有一些unity知识&#xff0c;包括Eidtor界面使用、Shader效果实现、性能分析&#xff0c;但对C#、游戏逻辑不太清楚&#xff0c;这次想从开发者角度理解游戏&#xff0c;提高C#编程&#xff0c;从简单的unity游戏理解游戏逻辑&#xff0c;更好的为工作服务。 unity201…

AWS创建实例 启用/禁用 自动分配公有 IP

给AWS新账户做完了对等连接&#xff0c;因为默认VPC网段都冲突 就换了VPC&#xff0c;然后发现新VPC内创建的实例都没有分配公网IP地址&#xff0c;自动分配公网IP地址变成了禁用。后续建机子需要手动修改成启用太麻烦了。 在VPC里面找到编辑子网设置&#xff0c;勾上启用自动…

【电子元件】常用电子元器件的识别之电阻器

目录 前言1. 电阻器的识别1.1 普通电阻器的识别1. 普通电阻器的识别色环电阻器绕线电阻器水泥电阻器贴片电阻器网络电阻器(排阻)保险电阻器精密电阻器2. 电阻器的符号3. 普通电阻器的主要参数标称阻值和允许误差额定功率最高工作电压温度系数1.2 电位器的识别1. 电位器的识别…

【C++初阶】C++STL详解(四)—— vector的模拟实现

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;C初阶 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 【C初阶】CSTL详解&#xff08;三…

全国职业技能大赛云计算--高职组赛题卷①(容器云)

全国职业技能大赛云计算--高职组赛题卷①&#xff08;容器云&#xff09; 第二场次题目&#xff1a;容器云平台部署与运维任务1 Docker CE及私有仓库安装任务&#xff08;5分&#xff09;任务2 基于容器的web应用系统部署任务&#xff08;15分&#xff09;任务3 基于容器的持续…

给yarn/npm包管理设置代理加速nodejs依赖下载的方法

由于墙内网络干扰屏蔽国外网络的原因&#xff0c;在国内下载nodejs依赖是非常缓慢的。 所以为了解决这个问题&#xff0c;必须设置代理&#xff0c;具体方法如下&#xff1a; NPM设置代理&#xff1a; npm config set proxy<http_proxy> NPM删除代理&#xff1a; npm c…

Java8中判断一个对象不为空存在一个类对象是哪个

Java8中判断一个对象不为空存在一个类对象是哪个&#xff1f; 在Java 8中&#xff0c;你可以使用java.util.Optional类来处理可能为空的对象。Optional类可以帮助你优雅地处理空值情况&#xff0c;而不需要显式地进行空值检查。 这是一个简单的Optional示例&#xff1a; imp…

「UG/NX」Block UI 指定矢量SpecifyVector

✨博客主页何曾参静谧的博客📌文章专栏「UG/NX」BlockUI集合📚全部专栏「UG/NX」NX二次开发「UG/NX」BlockUI集合「VS」Visual Studio「QT」QT5程序设计「C/C+&#

基于Java+SpringBoot+Vue的图书借还小程序的设计与实现(亮点:多角色、点赞评论、借书还书、在线支付)

图书借还管理小程序 一、前言二、我的优势2.1 自己的网站2.2 自己的小程序&#xff08;小蔡coding&#xff09;2.3 有保障的售后2.4 福利 三、开发环境与技术3.1 MySQL数据库3.2 Vue前端技术3.3 Spring Boot框架3.4 微信小程序 四、功能设计4.1 主要功能描述 五、系统实现5.1 小…

什么是Webpack的Tree Shaking?它的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ Webpack的Tree Shaking⭐ 作用和原理⭐ 使用 Tree Shaking⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感…

【vue组件】使用element-ui table 实现嵌套表格 点击展开时获取数据

应用场景是这样 主表格的数据是所有的学校 然后点击展开的时候&#xff0c;获取学校下相应班级的数据 并且班级要能选择后生成图表&#xff0c;但所有的班级最多选择5个 首先是嵌套表格 <div><el-table:data"tableDisplayData"id"chartTableExpand&q…

Nacos注册中心

Nacos 安装 https://nacos.io/zh-cn/ 源码安装 第一步&#xff1a;利用Gitee获取nacos在github上的代码到自己的gitee仓库中 https://github.com/alibaba/nacos.git 第二步&#xff1a;下载源码到本地。 第三步&#xff1a;使用maven编译代码。 # 先切换到master分支 gi…

ElasticSearch(ES)简单介绍

ES简介 Elasticsearch&#xff08;通常简称为ES&#xff09;是一个开源的分布式搜索和分析引擎&#xff0c;旨在处理各种类型的数据&#xff0c;包括结构化、半结构化和非结构化数据。它最初是为全文搜索而设计的&#xff0c;但随着时间的推移&#xff0c;它已经演变成一个功能…

JUnit测试进阶(Private测试)

Private测试 前言一、间接调用二、Java反射机制调用 前言 在单元测试中&#xff0c;由于私有方法&#xff08;Private Method&#xff09;无法直接被调用&#xff0c;因此对私有方法进行测试成为一项难题。一个可行的方法是&#xff1a;在测试时将私有方法改变为公有方法&…