当前位置: 首页 > news >正文

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指针,因此题目二会发生空指针的解引用,引起程序崩溃。

相关文章:

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

CSDN的uu们&#xff0c;大家好。这里是C入门的第九讲。 座右铭&#xff1a;前路坎坷&#xff0c;披荆斩棘&#xff0c;扶摇直上。 博客主页&#xff1a; 姬如祎 收录专栏&#xff1a;C专题 目录 1. 面向过程与面向对象 2. 类的定义 3. 类中的访问限定符 3.1 访问限定符的…...

【深度学习】 Python 和 NumPy 系列教程(十七):Matplotlib详解:2、3d绘图类型(3)3D条形图(3D Bar Plot)

目录 一、前言 二、实验环境 三、Matplotlib详解 1、2d绘图类型 2、3d绘图类型 0. 设置中文字体 1. 线框图 2. 3D散点图 3. 3D条形图&#xff08;3D Bar Plot&#xff09; 一、前言 Python是一种高级编程语言&#xff0c;由Guido van Rossum于1991年创建。它以简洁、易读…...

基于Spring Boot+vue的酒店管理系统

文章目录 项目介绍主要功能截图:前台后台部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于Spring Boot+vue的酒店管理…...

Python 通过threading模块实现多线程

视频版教程 Python3零基础7天入门实战视频教程 我们可以使用threading模块的Thread类的构造器来创建线程 def _ init _(self, groupNone, targetNone, nameNone, args(), kwargsNone, *, daemonNone): 上面的构造器涉及如下几个参数。 group:指定该线程所属的线程组。目前该…...

用一个RecyclerView实现二级评论

先上个效果图&#xff08;没有UI&#xff0c;将就看吧&#xff09;&#xff0c;写代码的整个过程花了4个小时左右&#xff0c;相比当初自己开发需求已经快了很多了哈。 给产品估个两天时间&#xff0c;摸一天半的鱼不过分吧&#xff08;手动斜眼&#xff09; 需求拆分 这种大家…...

音视频 SDL简介

一、SDL简介 SDL&#xff08;Simple DirectMedia Layer&#xff09;是一套开放源代码的跨平台多媒体开发库&#xff0c;使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数&#xff0c;让开发者只要用相同或是相似的代码就可以开发出跨多个平台&#xff08;Linux、Win…...

7.前端·新建子模块与开发(自动生成)

文章目录 学习地址视频笔记自动代码生成模式开发增删改查功能调试功能权限分配 脚本实现权限分配 学习地址 https://www.bilibili.com/video/BV13g411Y7GS/?p15&spm_id_frompageDriver&vd_sourceed09a620bf87401694f763818a31c91e 视频笔记 自动代码生成模式开发 …...

Linux 创建目录

语法&#xff1a;mkdir xxx Linux路径 在当前目录下创建文件夹 在/目录下创建文件夹 如果想要一次性创建多个层级的目录&#xff0c;如下图 会报错&#xff0c;因为上级目录test并不存在&#xff0c;所以无法创建test目录 可以通过-p选项&#xff0c;将一整个链条都创建完成…...

【DIY小记】修复Win10启动出现蓝屏0xc0000185错误的一些方法

近些日子想到自己尘封已久的笔记本电脑没有开机了&#xff0c;很多软件驱动之类的没有更新&#xff0c;就打算把电脑开起来做一轮批量升级。但开电脑的时候很久没有进入Win10桌面&#xff0c;等了很长一段时间蓝屏提示0xc0000185错误&#xff0c;说系统需要恢复。经历了一番折腾…...

Linux 下的 10 个 PDF 软件

本文[1]是我们正在进行的有关 Linux 顶级工具系列的延续&#xff0c;在本系列中&#xff0c;我们将向您介绍最著名的 Linux 系统开源工具。 随着互联网上越来越多地使用可移植文档格式 (PDF) 文件来获取在线书籍和其他相关文档&#xff0c;拥有 PDF 查看器/阅读器对于桌面 Linu…...

浅谈redis分布式锁

浅谈redis分布式锁 分布式锁介绍 分布式锁&#xff0c;顾名思义&#xff0c;分布式系统中的锁&#xff0c;当多个进程不在同一个系统中时&#xff0c;用分布式锁控制各个进程对共享资源的访问&#xff0c;通过互斥来保持一致性。 使用场景&#xff1a;电商中某商品的秒杀活动…...

【Python保姆级教程】List容器

文章目录 前言一、列表是什么二、列表的定义2.1 有初始值2.2 空列表使用方括号创建空列表使用list()函数创建空列表 三、list列表常用操作3.1 添加元素3.2 删除元素3.3 修改元素3.4 列表长度 四、遍历操作4.1 使用for循环4.2 使用while循环和索引 总结 前言 Python是一种广泛使…...

微服务保护-授权规则

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

v-if失效原因

一般v-if失效都是和绑定变量有关&#xff0c;我所知道的一般有两种 1.绑定的变量为String类型或者其他类型 就是返回的变量类型与所需要的布尔类型不匹配。 <template><div><div id"container" ref"container" v-iftype></div>&l…...

Chrome 基于 Wappalyzer 查看网站所用的前端技术栈

1. 找到谷歌商店 https://chrome.google.com/webstore/search/wappalyzer?utm_sourceext_app_menu 2. 搜索 Wappalyzer 3. 添加至Chrome 4. 使用 插件 比如打开 https://www.bilibili.com/ 就可以看到其所以用的前端技术栈了...

python的装饰器

作用:在不改变原来函数的代码情况下,进行修改,或者增加函数的功能装饰器本质上就是一个闭包雏形:def wrapper(fn): wrapper: 装饰器 , fn: 目标函数def inner():# 在目标函数执行前的一些动作fn()# 在目标函数执行后的一些动作return inner #千万别加(),这里是返回一…...

P2P协议的传输艺术

TP 采用两个 TCP 连接来传输一个文件。 控制连接&#xff1a;服务器以被动的方式&#xff0c;打开众所周知用于 FTP 的端口 21&#xff0c;客户端则主动发起连接。该连接将命令从客户端传给服务器&#xff0c;并传回服务器的应答。常用的命令有&#xff1a;list——获取文件目…...

辅助驾驶功能开发-功能规范篇(21)-4-XP行泊一体方案功能规范

XPilot Parking 自动泊车系统 • 超级自动泊车辅助(Super AutoParking Assist)、语音控制泊车辅助(Autoparking with Speech) - 产品定义 超级自动泊车辅助是⼀个增强的自动泊车辅助系统。在超级自动泊车辅助系统中,识别车位将会变得实时可见, 并且不可泊入的⻋位也将…...

家政服务小程序上门服务小程序预约上门服务维修保洁上门服务在线派单技师入口

套餐一:源码=1500元 套餐二:全包服务 包服务器+域名+认证小程序+搭建+售后=2000元 主要功能: 1、服务商入驻 支持个人或企业入驻成为平台服务商; 2、发布商品 入驻服务商后,可以发布服务商品,用户可以在线下单,预约服务; 3、发布需求 用户可以发布一口价或竞价需求…...

LeetCode精选100题-【3数之和】-2

这里写自定义目录标题 解法1:解法2: 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。注意&#xff1a;答案中不…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

Python 实现 Web 静态服务器(HTTP 协议)

目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1&#xff09;下载安装包2&#xff09;配置环境变量3&#xff09;安装镜像4&#xff09;node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1&#xff09;使用 http-server2&#xff09;详解 …...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号&#xff0c;此时&#xff0c;我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...