【C++杂货铺】详解类和对象 [上]

                                        博主:代码菌@-CSDN博客

                                        专栏:C++杂货铺_代码菌@的博客-CSDN博客


目录

🌈前言🌈

📁 面向对象语言的特性

📁 类

 📂 概念

 📂 定义

📁 访问限定符

 📂分类

 📂 封装

📁 类的作用域

📁 类的实例化

📁 类的模型

📂 类的存储

📁 面试题

📁this指针(灰常重要)

 📂 this指针的引入

 📂 this指针的特性

 📂面试题

📁 总结


🌈前言🌈

        欢迎收看本期【C++杂货铺】,这期内容,我们将围绕C++中类和对象部分内容进行讲解,包括了 封装,类的概念,如何定义类和类的大小等内容,最后我们给出工作面试出的一道题目进行讲解。

        如果你想学习C++,或者刚学完C语言衔接C++,那么这篇文章将会非常有效的帮助你理解。

📁 面向对象语言的特性

        面向对象语言有三大特性:封装,继承,多态。在类和对象中,我们重点讲解一下,什么是封装。

        例如,我们使用电脑,用户只需要知道如何与鼠标,键盘等进行交互,而不需要知道机器内部CPU,内存等是如何工作的,厂商都将其隐藏起来,封装在机箱中,这就是封装的过程。而这个机箱就是 类。

📁 类

 📂 概念

        不知道你是否学习过C语言中的结构体struct,来存储不同类型的数据。类就是结构体的进阶版,类 中不仅可以定义变量(成员变量),还可以定义 函数(成员函数)。

 📂 定义

class className
{// 类体:由成员函数和成员变量组成 ...
};struct Name
{// 类体:由成员函数和成员变量组成...
}

        需要注意的是类,不仅可以是class,也可以是struct。class定义时{}后面有 ; ,这点需要注意。

        class为定义类的关键字,ClassName为类的名字,{}内为类的主体。

        类体中的内容称为类的成员:类中的变量称为类的属性或者成员变量;类中的函数称为类的方法或成员函数

        类的两种定义方式:

        1. 声明和定义都在类体中,需要注意的是:成员函数如果在类中定义,编译器可能会将其当做是内联函数处理。(如果不懂内联函数,可以参考下面这篇文章)

【C++杂货铺】三分钟彻底搞懂函数重载-CSDN博客

        下图中,public 和 private是限定操作符。

        2. 类的声明放在 .h 文件中, 成员函数定义在 .cpp文件中。

        一般情况是,在学习竞赛中使用第一种情况;工作过程,采用第二种。

        此外,还有一个约定俗成,就是成员变量一般以 _开头,也是为了方便区分。

📁 访问限定符

        什么是访问限定符呢,就是一种限制。例如,我们的电脑,对于一个小白,那就限制他只能使用鼠标键盘等操作,不允许触碰机器内部的元器件。鼠标键盘就是共有的,机器元器件就是私有的。

 📂分类

【 访问限定符说明 】:

1. public 修饰的成员在类外可以直接被访问。
2. protected private 修饰的成员在类外不能直接被访问 ( 此处 protected private 是类似的 )。
3. 访问权限 作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。
4. 如果后面没有访问限定符,作用域就到 } 即类结束。
5. class 的默认访问权限为 private struct public( 因为 struct 要兼容 C)。
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

        

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

(注意:在继承和模板参数列表位置也有区别,后续给大家介绍)

 📂 封装

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

封装具有一下特性:

        1. C++中数据和方法都在类中

        2. 通过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 A1 {
public:void f1(){}
private:int _a;
};
// 类中仅有成员函数
class A2 {
public:void f2() {}
};
// 类中什么都没有---空类
class A3
{};
sizeof(A1) : ___4_ __    sizeof(A2) : ___1_ __     sizeof(A3) : ___1_ __
        这也说明,如果没有成员变量,类的大小为1Byte。
结论:一个类的大小,实际就是该类中 成员变量 之和,当然要注意内存对齐
注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。

📁 面试题

1. 结构体怎么对齐? 为什么要进行内存对齐?
        (1). 第一个成员在与结构体偏移量为 0 的地址处。
        (2). 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
        注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
        VS中默认的对齐数为8
        (3). 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数    倍。
        (4). 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
2. 如何让结构体按照指定的对齐参数进行对齐?能否按照3、4、5即任意字节对齐?
 #pragma pack( ~ )  这个预处理指令,可以改变编译器的默认对齐数。

📁this指针(灰常重要)

 📂 this指针的引入

        如果我们实例化两个相同类的对象A,B。同时调用成员函数初始化和打印这两个对象,如下。

class Date
{
private:int _year;int _month;int _day
public:void Init(int year,int month,int day){_yeaar = year;_month = month;_day = day;}void Print(){cout<<_year<<'/'<<_month<<'/'<<_day<<endl;}
}int main()
{Date A;A.Init(1,1,1);A.Print();Date B;B.Init(2,2,2);B.Print();
}

        编译器如何识别A和B呢?通过类的模型,我们知道,A和B的所调用的函数都是同一个,都是执行相同的指令,那编译器怎么识别year,month,day是A还是B的成员变量了。

        你会发现,谁调用了,就打印,赋值给谁的成员变量。

        这就引入了this指针的概念,隐含的this指针。所有的成员函数都隐藏一个形参,调用的地方,编译器也会处理。

 void Print(Date* const this)
{cout<<_year<<'/'<<_month<<'/'<<_day<<endl;
}A.Print(&A);

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

 📂 this指针的特性

        1. this指针的类型 类型 * const 。即在成员函数中,this1指针不能被修改指向。

        2. 在形参和实参的位置不能显示写。即你不能写this,这是编译器做的事,你做了,编译器会报错。

        3. this指针只能在成员函数中内部使用。

        4. this指针的本质是 成员函数的形参,当对象调用成员函数时,将对象地址作为实参传递给this这个形参,所以对象不存储this指针

        5. this指针是成员函数第一个隐含的指针形参,一般情况下由编译器通过ecx寄存器自动传递,不需要用户传递。

 📂面试题

this指针存在在哪里?B

A. 堆                B. 栈        C. 静态区        D. 常量区          E. 对象里面        

解析:

        首先排除E,因为对象实例化后,只存储成员变量,这是在类的模型中讲解的。

        其次,排除常量区,cosnt修饰的常变量本质上还是变量。

        然后,排除静态区,静态区只存储 static静态变量和全局变量。

        最后,排除堆,堆是程序员自己开辟管理的空间。

this指针可以为空吗:可以,只要不在成员函数中解引用。

        我么创建一个类的空指针,空指针作为实参传递给形参。

        ( mov的意思就是数据赋值,lea的意思就是取地址。)

        

        下面为案例,成员函数不存储在对象中,所以A->Print不是解引用,编译器会识别成Date::Print即通过A的类找到Print这个成员函数。其他成员函数也是同理。

① 在成员函数不解引用this指针。

class Date
{
private:int _year;int _month;int _day
public:void Init(int year,int month,int day){_yeaar = year;_month = month;_day = day;}void Print(){cout<<_year<<'/'<<_month<<'/'<<_day<<endl;}
}
int main()
{Date* A = nullptr;
A->Print();
}

        上面代码,会将A的值传递给this指针,但在Print中并没有解引用空指针,所以程序正常运行。

② 如果this指针为空,在成员函数解引用this指针。会发生运行错误。

class Date
{
private:int _year;int _month;int _day
public:void Init(int year,int month,int day){_yeaar = year;_month = month;_day = day;}void Print(){cout<<_year<<'/'<<_month<<'/'<<_day<<endl;}
}int main()
{Date* A = nullptr;
A->Init();
}

        上面代码,会将A的值传递给this指针,但在Init中解引用空指针,所以程序运行错误。

        总结一下,可以定义了一个类的指针为空指针,通过空指针,可以找到类的成员函数,此时,并不是空指针的解引用,此外,空指针将赋值给this指针。

        this指针可以为空,但在成员函数中解引用this指针会运行错误。

📁 总结

        总结,类就类似于struct结构体,不过可以再内部定义成员函数。

        通过学习类,了解什么是封装,即通过类将对象的属性和方法结合在一起,通过访问限定符有选择的接口提供给外部用户使用。封装的本质就是一种管控。

        当然这只是类的初窥门槛,之后的文章将逐渐揭开类的神秘面纱。

        如果这篇文章对你有所收获,欢迎点赞,评论,收藏。Thanks♪(・ω・)ノ

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

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

相关文章

立创EDA/PCB使用下单助手下单

优惠券领取 首先下载嘉立创订单助手&#xff0c;在助手里面领取并使用 这里我已经领取了 打开下单助手-PCB/FPB下单 或者 上传PCB文件 下单 等待gerber就绪后&#xff0c;点击立即下单 选择该项 点击开始下单 提交订单 按规则排除错误&#xff0c;没有问题后&#x…

.NET高级面试指南专题一【委托和事件】

在C#中&#xff0c;委托&#xff08;Delegate&#xff09;和事件&#xff08;Event&#xff09;是两个重要的概念&#xff0c;它们通常用于实现事件驱动编程和回调机制。 委托定义&#xff1a; 委托是一个类&#xff0c;它定义了方法的类型&#xff0c;使得可以将方法当作另一个…

高中数学:集合

一、基本概念与关系 1、元素 2、集合 集合中元素的特性 1、确定性。2、无序性。3、互异性。 3、空集&#xff0c;用∅符号表示 4、元素与集合的关系是属于关系&#xff0c;用∈符号表示 5、集合与集合的关系是包含关系。用⊆或者⊊符号表示 子集与真子集。 A⊊B > A⊆B 反之…

windows系统中如何安装tomcat

在 Windows 系统中安装 Tomcat 通常涉及以下步骤: 1.下载 Tomcat: 访问 Apache Tomcat 官方网站。在 "Download" 部分找到适用于 Windows 的最新版本的 Tomcat。下载 Core 二进制分发版(通常是.zip文件)。 2.解压 Tomcat 压缩包: 将下载的.zip文件解压到…

Linux使用二进制包安装MySQL

目录 一、软件包下载 二、上传软件包到Linux根目录 1、使用xftp将软件包上传到根目录 2、解压缩 三、准备工作 四、初始化软件 五、设置MySQL的配置文件 六、配置启动脚本 一、软件包下载 官网下载&#xff1a;MySQL :: Download MySQL Community Server 二、上传软件…

Python第十二章(函数)

一。函数的创建规则必须是&#xff1a;先创建后调用&#xff08;当调用函数的时候&#xff0c;解释器回到函数定义的地方去执行下方缩进的代码&#xff0c;再进行函数下的内容&#xff09; 定义函数语法&#xff1a;def 函数名(参数)&#xff1a; 代码1..... 调用函数的语法&…

使用make_grid多批次显示网格图像(使用CIFAR数据集介绍)

背景介绍 在机器学习的训练数据集中&#xff0c;我们经常使用多批次的训练来实现更好的训练效果&#xff0c;具体到cv领域&#xff0c;我们的训练数据集通常是[B,C,W,H]格式&#xff0c;其中&#xff0c;B是每个训练批次的大小&#xff0c;C是图片的通道数&#xff0c;如果是1…

OpenHarmony关系型数据库

1 概述 关系型数据库(Relational Database, 以下简称RDB)是一种基于关系模型来管理数据的数据库&#xff0c;是在SQLite基础上提供一套完整的对本地数据库进行管理的机制&#xff0c;为开发者提供无需编写原生SQL语句即可实现数据增、删、改、查等接口&#xff0c;同时开发者也…

2023.1.23 关于 Redis 哨兵模式详解

目录 引言 人工恢复主节点故障 ​编辑 主从 哨兵模式 Docker 模拟部署哨兵模式 关于端口映射 展现哨兵机制 哨兵重新选取主节点的流程 哨兵模式注意事项 引言 人工恢复主节点故障 1、正常情况 2、主节点宕机 3、程序员主动恢复 先看看该主节点还能不能抢救如果不好定…

Vue中使用TypeScript:全面指南和最佳实践

🚀 欢迎来到我的专栏!专注于Vue3的实战总结和开发实践分享,让你轻松驾驭Vue3的奇妙世界! 🌈✨在这里,我将为你呈现最新的Vue3技术趋势,分享独家实用教程,并为你解析开发中的难题。让我们一起深入Vue3的魅力,助力你成为Vue大师! 👨‍💻💡不再徘徊,快来关注…

首发:2024全球DAO组织发展研究

作者&#xff0c;张群&#xff08;专注DAO及区块链应用研究&#xff0c;赛联区块链教育首席讲师&#xff0c;工信部赛迪特邀资深专家&#xff0c;CSDN认证业界专家&#xff0c;微软认证专家&#xff0c;多家企业区块链产品顾问&#xff09; DAO&#xff08;去中心化自治组织&am…

排序问题上机考试刷题

排序与查找可以说是计算机领域最经典的问题&#xff0c;排序和查找问题在考研机试真题中经常出现。排序考点在历年机试考点中分布广泛。排序既是考生必须掌握的基本算法&#xff0c;又是考生 学习其他大部分算法的前提和基础。首先学习对基本类型的排序。对基本类型排序&#x…