【C++练级之路】【Lv.2】类和对象(上)(类的定义,访问限定符,类的作用域,类的实例化,类的对象大小,this指针)

目录

  • 一、面向过程和面向对象初步认识
  • 二、类的引入
  • 三、类的定义
  • 四、类的访问限定符及封装
    • 4.1 访问限定符
    • 4.2 封装
  • 五、类的作用域
  • 六、类的实例化
  • 七、类的对象大小的计算
    • 7.1 类对象的存储方式猜测
    • 7.2 如何计算类对象的大小
  • 八、类成员函数的this指针
    • 8.1 this指针的引出
    • 8.2 this指针的特性
  • 总结与对比

欢迎各位小伙伴关注我的专栏,和我一起系统学习C++,共同探讨和进步哦!

学习专栏

《进击的C++》


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

C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。

拿出盆子
加水
加入洗衣粉
加入衣服
手搓

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

洗衣粉
洗衣机
衣服

二、类的引入

C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。

比如:用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,会发现struct中也可以定义函数

struct Stack
{void Init(int capacity = 4){int* tmp = (int*)malloc(sizeof(int) * capacity);if (tmp == nullptr){perror("malloc fail");return;}_a = tmp;_capacity = capacity;_top = 0;}//...int* _a;int _top;int _capacity;
};

简单理解:C中的结构体,C++将其升级成了类

  • 上面结构体的定义,在C++中更喜欢用class来代替

三、类的定义

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

类的两种定义方式

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

stack.h

class Stack
{void Init(int capacity = 4){int* tmp = (int*)malloc(sizeof(int) * capacity);if (tmp == nullptr){perror("malloc fail");return;}_a = tmp;_capacity = capacity;_top = 0;}//...int* _a;int _top;int _capacity;
};
  1. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意成员函数名前需要加类名::

stack.h

class Stack
{void Init(int capacity = 4);//...int* _a;int _top;int _capacity;
};

stack.cpp

void Stack::Init(int capacity)
{int* tmp = (int*)malloc(sizeof(int) * capacity);if (tmp == nullptr){perror("malloc fail");return;}_a = tmp;_capacity = capacity;_top = 0;
}
  • 一般正式的工程中,都使用第二种声明和定义分类的写法,所以推荐往后都用第二种定义方法。

成员变量命名规则的建议

  • 建议加上前缀或者后缀,以示区分成员函数的形参。

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

4.1 访问限定符

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

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

注意访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

【面试题】
问题:C++中struct和class的区别是什么?

解答:C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类默认访问权限是private。(注意:在继承和模板参数列表位置,struct和class也有区别,后序给大家介绍。)

4.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 Date
{
public:void Init(int year, int month, int day);private:int _year;int _month;int _day;
};int main()
{Date d1;//类的实例化return 0;
}

七、类的对象大小的计算

问题:类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算一个类的大小?

7.1 类对象的存储方式猜测

  1. 对象中包含类的各个成员

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

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

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


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

7.2 如何计算类对象的大小

我们进一步分析以下代码:

// 类中既有成员变量,又有成员函数
class A1
{
public:void f1() {}
private:int _a;
};// 类中仅有成员函数
class A2
{
public:void f2() {}
};// 类中什么都没有---空类
class A3
{};int main()
{cout << sizeof(A1) << endl;cout << sizeof(A2) << endl;cout << sizeof(A3) << endl;return 0;
}

结论:一个类的大小,实际就是该类中”成员变量”之和(猜测3),当然要注意内存对齐(详情可见往期自定义类型:结构体(你真的掌握了内存对齐,位段吗?))

注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。

八、类成员函数的this指针

8.1 this指针的引出

我们先来定义一个日期类 Date

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(2023, 12, 3);d1.Print();d2.Init(2022, 1, 3);d2.Print();return 0;
}

对于上述类,有这样的一个问题:

Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?

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

请看以下画图分析:

编译器处理前:

编译器处理后:

实际上,底层在汇编指令中,除了传三个参数,还将对象的地址传送过去(lea——load effective address 加载有效地址)。

8.2 this指针的特性

  1. this指针的类型类类型* const,即成员函数中,不能给this指针赋值。
void print()
{cout << typeid(this).name() << endl;cout << _year << "-" << _month << "-" << _day << endl;
}

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

【面试题】
1. this指针存在哪里?
2. this指针可以为空吗?

解答1:栈,因为this是隐含形参 / VS平台是通过ecx寄存器
解答2:请先分析下面代码:

// 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指针能不能为空,取决于实际需求是否需要解引用。代码1直接打印,则不需要解引用,所以能正常运行;而代码2要打印成员变量_a,则需要解引用,所以会崩溃。(所以下次遇到->操作符,不要认为一定会解引用哦)

总结与对比

C:

  1. 数据和方法是分离的
  2. 数据访问控制是自由的,不受限制的

C++:

  1. 数据和方法都封装在类里面
  2. 控制访问方式。愿意让你访问公有,不愿意让你访问私有。

看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注!
💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖
拜托拜托这个真的很重要!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。

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

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

相关文章

【算法】单调栈题单(矩阵系列、字典序最小、贡献法)⭐

文章目录 题单来源经典题单496. 下一个更大元素 I&#xff08;单调栈模板题&#xff09;503. 下一个更大元素 II&#xff08;单调栈循环数组&#xff09;2454. 下一个更大元素 IV&#xff08;第二个更大的元素&#xff1a;两个单调栈&#xff09;456. 132 模式&#xff08;单调…

操作系统||经典进程同步问题之读者-写者问题

一个数据文件或记录可被多个进程共享 【问题描述】 有读者和写者两组并发进程&#xff0c;共享一个文件&#xff0c;当两个或两个以上的读进程同时访问共享数据时不会产生副作用&#xff0c;但若有某个写进程和其他进程&#xff08;读进程或写进程&#xff09;同时访问共享数…

Autosar COM通信PDU

文章目录 Autosar 中各个PDU所在示意图PDU的分类PDU 和 SDU 的关系I-PDUN-PDUL-PDU相关协议其他参考 Autosar 中各个PDU所在示意图 PDU的分类 在Autosar 中&#xff0c;主要有 I-PDU、N-PDU和 L-PDU 三种。 L-PDU&#xff1a;Data Link Layer PDU&#xff0c;数据链路层PDUN-…

[C/C++]数据结构 关于二叉树的OJ题(利用分治思想解决难题)

题目一: 单值二叉树 &#x1f6a9;⛲&#x1f31f;⚡&#x1f966;&#x1f4ac; &#x1f6a9;题目链接:力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 ⛲题目描述: 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。…

制作飞腾(arm)芯片架构的nexus镜像

nexus官方没有arm架构的镜像&#xff0c;下面介绍一种自己制作镜像的方式 1、事先准备 在一个arm架构机器上安装docker下载nexus的linux版(https://www.sonatype.com/download-oss-sonatype)下载centos的arm架构镜像(docker pull centos-centos8.4.2105)下载arm版本的java8(ht…

POJ 3734 Blocks 动态规划(矩阵的幂)

一、题目大意 我们要给排成一行的区块涂颜色&#xff0c;可以选择红、绿、蓝、黄四种&#xff0c;要求红和绿的块都必须是偶数个&#xff0c;求出最终的涂色方式&#xff0c;对10007取余。 二、解题思路 我们设三个数列A&#xff0c;B和C&#xff1a; 1、A代表红色和绿色都…

SpringSecurity 三更草堂 学习笔记

SpringSecurity从入门到精通 0. 简介 Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的…

前端笔记(二):CSS 选择器与特性

CSS&#xff08;层叠样式表&#xff09;是一种样式表语言&#xff0c;用于描述HTML或XML文档的呈现方式。它定义了如何在屏幕、纸张或其他媒体上显示文档的样式、布局和外观。 里面的代码由 选择器 { } 组成 体验 CSS CSS 可以让我们界面变得更加美观&#xff0c;这是 CSS 的…

同旺科技 分布式数字温度传感器 -- Modbus Poll测试

内附链接 1、数字温度传感器 主要特性有&#xff1a; ● 支持PT100 / PT1000 两种铂电阻&#xff1b; ● 支持 2线 / 3线 / 4线 制接线方式&#xff1b; ● 支持5V&#xff5e;17V DC电源供电&#xff1b; ● 支持电源反接保护&#xff1b; ● 支持通讯波特率1200bps、2…

YOLOv8 区域计数 | 入侵检测 | 人员闯入

大家好,昨天的 YOLOv8 新增加了一个功能,区域计数,用这个功能我们能实现很多的任务, 比如入侵检测,流量统计,人员闯入等,使用方式也非常的方便,但是一定要使用最新版的 YOLOv8 代码(2023/12/03更新的代码)。 低版本是不具备这个功能的,上面是演示效果。 使用非常的方…

计算机网络TCP篇③问答篇

目录 一、如何理解 TCP 是面向字节流协议 先来说说为什么 UDP 是面向报文的协议&#xff1f; 如果收到了两个 UDP 报文&#xff0c;操作系统是如何区分开的呢&#xff1f; 再说说为什么 TCP 是面向字节流的协议&#xff1f; 二、如何解决粘包问题&#xff1f; ①、固定消…

【强化学习算法】Q-learning原理及实现

实现代码github仓库&#xff1a;RL-BaselineCode 代码库将持续更新&#xff0c;希望得到您的支持⭐&#xff0c;让我们一起进步&#xff01; 文章目录 1. 原理讲解1.1 Q值更新公式1.2 ε-greedy随机方法 2. 算法实现2.1 算法简要流程2.2 游戏场景2.3 算法实现 3. 参考文章 1. 原…

DBS note7 (end):DB Design

目录 一、前言 二、引言 三、Entity-Relationship Models&#xff08;实体-关系模型&#xff09; 1、关系约束 三、函数依赖和正则化 1、BCNF分解 2、无损分解 3、依赖关系保留分解 一、前言 略读过一遍CS186&#xff0c;对于CS186来说&#xff0c;绝对不止这 7 篇笔记…

10. 哈希表

哈希表(hash table)&#xff0c;又称散列表&#xff0c;其通过建立键 key 与值 value 之间的映射&#xff0c;实现高效的元素查询。具体而言&#xff0c;我们向哈希表输入一个键 key &#xff0c;则可以在 \(O(1)\) 时间内获取对应的值 value 。 给定 n 个学生&#xff0c;每个…

Docker Registry(镜像仓库)——4

目录&#xff1a; 什么是 Docker Registry镜像仓库生活案例镜像仓库分类镜像仓库工作机制常用的镜像仓库镜像仓库命令 命令清单docker logindocker pulldocker pushdocker searchdocker logout镜像命令[部分] docker imagesdocker image inspectdocker tag容器命令[部分] dock…

通义灵码简单使用例子

首先我们需要了解到通义灵码的能力&#xff1a; 行/函数级实时续写&#xff1a; 当我们在 idea进行代码编写时(确认开启了自动云端生成的模式)&#xff0c;通义灵码会根据当前代码文件及相关代码文件的上下文&#xff0c;自动为你生成代码建议。你可以不用&#xff0c;也可以t…

Verilog 入门(九)(建模实例)

文章目录 建模的不同方式条件操作建模通用移位寄存器状态机建模Moore 有限状态机建模Mealy 型有限状态机建模 建模的不同方式 分别使用数据流方式、行为方式和结构方式对下图所示的电路进行建模&#xff1a; 数据流方式&#xff1a;使用连续赋值语句对电路建模 module Save_M…

viple模拟器使用(四):unity模拟器中实现两距离局部最优迷宫算法

名字解读 两距离&#xff1a;指的是左侧距离和右侧距离 局部最优&#xff1a;对当前状态来说最好的选择&#xff0c;至于整体能不能达到最优&#xff0c;是无法确定的。 从节点1到节点5&#xff0c;一共有3条路 第1条路线&#xff1a;1→2→4→5&#xff0c;对应的花销是&…

机器学习笔记 - 什么是模型量化压缩技术?

一、简述 我们都知道现实世界是连续的状态,而计算机世界是离散的状态,这是什么意思呢?我们看一下下图,最右边的马力欧(高清)的状态,可以想象现实世界是连续的状态,而电脑世界在图像上呈现的是一格一格子的状态(左图)是离散的状态。 所以在计算机世界如果想要…

51单片机应用从零开始(十)·指针

指针 C语言指针是一种保存变量地址的数据类型。它可以让程序直接访问内存中的数据&#xff0c;而不需要通过变量名来访问。指针变量存储的是一个地址&#xff0c;这个地址指向内存中的某个位置&#xff0c;该位置存储了一个值。 在C语言中&#xff0c;可以使用&运算符取得一…