C++的第一道门坎:类与对象(一)

news/2024/7/25 13:38:57/文章来源:https://blog.csdn.net/duanku111/article/details/139231207

1.面向过程与面向对象

1.1面向过程

我们之前学习的C语言就是一种面向过程的语言,面向过程的语言强调的是具体实现的过程,一般用函数来具体实现。我们用面向过程的思想,就可以把炒菜分为以下几个步骤:

1.2面向对象

而对于面向对象的语言而言,它将问题分为了多个对象,问题的解决由对象之间的交互完成,而不再注重对象完成事情的具体过程,譬如上述炒菜的过程我们即可抽象为人、菜、锅的交互。

C++即是一门面向对象的语言,我们更强调对象之间的交互,而并不在意对象该如何解决问题。 

1.3对比

面向过程的语言和面向对象的语言各有其优缺点:

面向过程的语言流程化分工明确,效率高,但可维护性差,拓展能力差。

面向对象的语言结构化更清晰,易维护,但开销大,性能低。

2.类的引入

在C++中,我们在结构体的基础上引入了类(class)的概念,并对struct在CPP的基础上重新进行了解释。

由于我们的面向对象不注重过程的实现,但是对象本身还是需要实现过程的,因此我们要给对象加上一定的过程,比如我们的人锅交互可以炒菜,我们就要在类中声明一个作用是炒菜的函数。

也就是说,CPP的类和结构体中可以定义成员函数!

2.1类的声明方式

在C++中,有一个关键字为class,我们可以使用这个关键字来声明一个类,类内可以声明成员函数和成员变量,其语法结构为:

class 类名
{//类内的成员函数和成员变量
};

现在我们声明一个名为duanku的类:

class duanku
{int a;//成员变量int Add(int a, int b);//成员函数
};

2.2类的成员的两种定义方式

在定义类时,我们可以将类的定义与声明在同一文件书写,也可以在不同文件内书写。

2.2.1单文件定义

定义一个类,自然需要实现类体内的函数,我们可以将函数在类内定义,如下所示:

class duanku
{int a;//成员变量int Add(int a, int b)//成员函数{return a + b;}
};

这里需要大家注意的是:成员函数若在类内定义,则可能会被编译器当作内联函数处理。

2.2.2多文件定义

一个类也可以分为两个文件来实现,我们可以将成员函数的定义放在cpp文件内部。

但这儿需要大家注意的一点是:你定义的函数是谁的函数呢?是只在这个文件内部用的,还是实现了哪个地方的声明呢?因此,我们需要加域作用限定符,让编译器知道我们实现的是哪个函数。

//.h文件
class duanku
{int a;//成员变量int Add(int a, int b);//成员函数
};
//.cpp文件
int duanku::Add(int a, int b)
{return a + b;
}

 PS:在日常使用时,我们可以将定义和声明都放在类中。但是在实际的工程中,更倾向于将声明和定义分离。

3.类的访问限定符与封装

3.1访问限定符

在C++类中有三种访问限定符:public、private、protect

它们的作用如下

  • public修饰的成员可以在类外直接访问
  • private和protect修饰的成员在类外不可直接被访问(后续文章会详细解释private和protect)
  • 一个类中可以有多个访问限定符,一个限定访问符的作用域该限定访问符出现的位置到下一个限定访问符出现的位置,如果后面没有限定访问符,则到类结束的位置为止。
  • class的默认访问权限为private,struct的默认访问权限为public(下篇文章中会给出class和struct的详解)

3.2封装

封装:用类将对象的属性(数据)与操作数据的方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。

通俗的说,封装其实是一种管理,是为了方便让用户更方便的使用类的一种方法。

比如说:对于一台电脑而言,我们提供给用户的是一系列的USB接口,我们可以通过它们和计算机进行一系列的交互,也可以自由更改每个USB接口的接什么,但是我们无法更改电脑的硬件元件。

这里,我们可以这样理解:

每个USB接口都是一个函数,我们可以自由更改它们,它们的访问限定为public。

而硬件元件则是类中的私有成员,不让外界访问,它们的访问限定为private。

4.类对象

4.1类对象的实例化

在类中定义的成员变量实际上是一种声明,而我们利用类名字定义的对象就是类对象的实例化

即,用类的类型创造对象的过程,称为类的实例化

1.类是对对象进行描述的,是一个模型一样的东西,限定了类中有哪些成员,定义出一个类并不会分配实际的内存空间来存储它。

2.一个类可以实例化出多个对象,实例化出的对象才占用实际的内存空间,存储的是类的成员变量。

我们不可以直接利用类来进行定义,如下图所示

 正确的定义方法如下:

	Date a;a._year = 3;

4.2类对象的存储

我们已经知道了类对象的创建方式,那么具体类中的成员变量与成员函数又是如何存储的呢?

4.2.1 存储方式1

每次创建对象时,都开辟一个空间存储类中的成员变量与成员函数。

但是这样存储就会出现一个问题,成员函数都是用的一个函数,我们每次都创建一个岂不是会有很大的开销吗?一个就能行的事儿为什么要用很多个呢?

于是,我们的计算机中,大多数都不是这么存储对象的。

4.2.2 存储方式2

 我们首先将成员函数放在外面,之后我们每次创建对象时,都开辟一个空间存储类成员变量与成员函数的地址,需要成员函数的时候直接按照地址去找。

4.2.3存储方式3

每次创建对象时,都开辟一个空间存储类成员变量。而成员函数提前单独存储在另外一个区域(公共代码段)。

之后我们没去用到成员函数时,都去那个区域里面拿出来用。

一般的编译器采用的都是存储模式2或存储模式3,具体使用的是哪种存储模式,我们可以通过计算类大小来进行判断。 

4.3类对象的大小

4.3.1普通类对象的大小

类型对象的大小也遵循结构体的内存对齐规则:

1.类的第一个成员对齐到结构体变量起始位置偏移量为0的地址处

2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

   对齐数=编译器默认的一个对齐数与该成员变量大小的较小值

   --VS2022中的默认对齐数是8

   --Liunx中gcc没有默认对齐数,对齐数就是成员自身的大小

3.类总大小为所有成员对齐数中的最大对齐数的整数倍

4.如果嵌套了结构体,嵌套的结构体成员对齐到自己的成员的最大对齐数处,结构体的整体大小就是所有成员的最大对齐数(含嵌套结构体成员)的整数倍。

当然,类大小的计算我们可以也借助sizeof计算。

#include<iostream>
using namespace std;
class Date
{
//可以被直接访问
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}
//不能被直接访问
private:int _year;int _month;int _day;
};
int main()
{Date d;cout << sizeof(d) << endl;return 0;
}

 

由此可见,我们这里算出来的12是三个int类型的总和,而我们的成员函数则被放到了公共代码段中。 若是使用的存储模式2的话,则应该是12+成员函数地址的大小(32位系统是4,64位系统是8)。

 4.3.2空类的计算

当类中只有成员函数,或者什么都没有时,类的大小是多少呢?

class A
{
public:void func(){;}
};
class B
{
};
int main()
{A a;B b;cout << sizeof(a)<<endl << sizeof(b);return 0;
}

 

这里我们发现空类的大小为1,现在我们可以思考这么一个问题,为什么空类的大小是1而不是0呢? 这是因为我们在进行空类的实例化时必须要有空间表示我们在这儿存储了一个对象。这时编译器就会默认给一个字节大小来标记这个对象

5.this指针

5.1this指针的由来

#include<iostream>
using namespace std;
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;
}

在这里我先给大家抛出这么一个问题:为什么d1调用Init函数时,该函数知道是给d1对象调用Init,而不是给d2对象调用Init呢? 

在C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数。通过不同的对象地址来分辨不同的对象,只不过所有的操作对用户是透明的的,即用户不需要来传递,编译器自动完成。实际代码如下:

void Init(Date* this, int year, int month, int day)
{this->_year = year;this->_month = month;this->_day = day;
}
d1.Init(&d1,2022, 1, 11);//实际传参

5.2this指针的特点

  1. this指针额类型为const,即我们无法给this指针赋值
  2. this指针的本质是“成员函数”的形参,当对象调用成员函数时,会将对象的地址当作实参传递给形参。
  3. this指针只能在成员函数中使用
  4. this指针是“成员函数”第一个隐藏起来的指针形参,一般由编译器放在通过eax寄存器自动传递,不需要用户传递

5.3两个问题

5.3.1问题1

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

这段代码是可以正常运行的,虽然p是一个空指针,但是由于我们的成员函数放在公共代码段中,因此我们还是可以找到这个函数的,不会因为对空指针解引用而找不到它。

5.3.2问题2

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

这里就会让程序崩溃,因为函数中访问了_a ,nullptr->_a程序就崩溃了。

 6.成员变量的命名

大家在使用类时,可能会出现以下这么一个问题:

这时为了解决问题这类问题,我们在定义成员变量时会对其进行特定地修饰。比如说_变量名或者变量名_。不同公司,不同的程序员可能都有一套自己的命名规则,这里我们采用第一种。

由此我们可以将上述代码改写为这样:

class Date
{void Init(int year, int month, int day){_year = year;_month = month;_day = day;}int _year;int _month;int _day;
};

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

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

相关文章

【ARMv8/v9 异常模型入门及渐进 10 -- WFI 与 WFE 使用详细介绍 1】

请阅读【ARMv8/v9 ARM64 System Exception】 文章目录 WFI 与 WFE等待事件&#xff08;WFE&#xff09;发送事件&#xff08;SEV&#xff09;本地发送事件&#xff08;SEVL&#xff09;WFE 唤醒事件 WFE 使用场景举例与代码实现wfe睡眠函数sev 事件唤醒函数全局监视器和自旋锁 …

如何异地组网添加摄像机?

本文将介绍如何使用天联技术实现异地组网添加摄像机&#xff0c;并保障数据的安全性。 安防摄像机的应用愈发广泛&#xff0c;无论是家庭安防还是企业监控&#xff0c;摄像机都扮演着重要角色。在一些特殊场合或者特殊需求下&#xff0c;我们需要将摄像机添加到异地网络中进行监…

uniapp android使用uni.chooseLocation,app云打包后,定位地址列表一直在加载中

复现BUG 1、自己生成一个证书 参考生成证书流程 2、使用刚生成证书的SHA1 &#xff0c;重新创建一个高德key 高德开放平台地址 3、打包&#xff08;打包的包名要与高德申请key所填的包名一致&#xff09;

Java面试八股之Synchronized锁升级的原理

Synchronized锁升级的原理 Synchronized锁升级是Java为了提高并发性能而引入的一项优化措施&#xff0c;这一机制主要发生在JDK 1.6及之后的版本中。Synchronized锁升级旨在减少锁带来的性能开销&#xff0c;通过从低开销的锁逐步升级到高开销的锁&#xff0c;以适应不同的竞争…

从零入门激光SLAM(二十一)——FAST-LIO2论文解析

FAST-LIO2: Fast Direct LiDAR-Inertial Odometry 论文地址&#xff1a;https://ieeexplore.ieee.org/stamp/stamp.jsp?tp&arnumber9697912 代码&#xff1a;https://github.com/hku-mars/FAST_LIO 一、文章概述 1.问题导向 基于视觉传感器的高分辨率和高精度的实时密…

Docker笔记-搭建达梦Python环境(dmPython + SQLAlchemy)

背景 达梦提供的C接口&#xff0c;dpi和java的jar包已经很好用了&#xff0c;想不到&#xff0c;来了一个用python的同事&#xff0c;这里就只能适应下他了&#xff0c;在不影响其他环境下搭建一个python的达梦环境。最后发现&#xff0c;python对进行达梦增删改查&#xff0c…

【设计模式】JAVA Design Patterns——Bytecode(字节码模式)

&#x1f50d;目的 允许编码行为作为虚拟机的指令 &#x1f50d;解释 真实世界例子 一个团队正在开发一款新的巫师对战游戏。巫师的行为需要经过精心的调整和上百次的游玩测试。每次当游戏设计师想改变巫师行为时都让程序员去修改代码这是不妥的&#xff0c;所以巫师行为以数据…

1 计算机硬件-CPU-校验码-存储系统-输入输出设备-总线结构

计算机硬件 考情分析&#xff1a;趋势很小&#xff0c;22年考过&#xff0c;根据趋势以后考的可能较小 基本组成&#xff1a;运算器&#xff0c;控制器&#xff0c;储存器&#xff0c;输入设备&#xff0c;输出设备运算器和控制器也统称为中央处理单元&#xff08;CPU&#xf…

视频截图软件,这几款截图神器收好!

在数字化时代&#xff0c;视频内容已经成为我们获取信息、娱乐休闲的主要方式之一。而在观看视频的过程中&#xff0c;我们总会遇到一些想要定格下来的精彩瞬间。此时&#xff0c;一款高效的视频截图软件就显得尤为重要。今天&#xff0c;就为大家推荐几款功能强大、操作简便的…

2024年5月软考,别再傻傻啃书了!

备考2024年软考&#xff0c;不听课也不刷题&#xff0c;只是看教材的话&#xff0c;想要考试通过&#xff0c;几乎是不可能的&#xff0c;特别是基础比较薄弱的考生。 为什么只看教材通不过&#xff1f; 如果只是把教材从头到尾看一遍&#xff0c;毫无目的地看书&#xff0c;…

【傻呱呱】VirtualHere共享局域网中的USB设备(使用Pavadan老毛子固件搭建篇)

前期准备 SSH工具&#xff08;FinalShell&#xff09;老毛子固件路由器一台 搭建VirtualHere服务端 进入VirtualHere官网下载对应处理器架构的包&#xff0c;我的是RT-N14U-GPIO路由器刷的老毛子固件&#xff0c;这种一般选择最后一个或者倒数第二个包&#xff0c;这里我选择…

Java三种方法实现多线程,继承Thread类,实现Runnable接口,实现Callable接口

目录 线程&#xff1a; 继承Thread类&#xff1a; 实现Runnable类&#xff1a; 实现Callable接口&#xff1a; 验证多线程&#xff1a; 线程&#xff1a; 定义&#xff1a;进程可以同时执行多个任务&#xff0c;每个任务就是线程。举个例子&#xff1a;一个Java程序&#…

23种设计模式顺口溜

口诀&#xff1a; 原型 抽风 &#xff0c;单独 建造 工厂 &#xff08;寓意&#xff1a;&#xff08;这里代指本来很简单的东西&#xff0c;却要干工厂这里复杂的业务&#xff09; 抽风&#xff1a;抽象工厂单独&#xff1a;单例桥代理组合享元适配器&#xff0c;&#xff0…

Modbus协议基础

文章目录 Modbus概述Modbus TCP/IP Modbus概述 Modbus是由Modicon&#xff08;现为施耐德电气公司的一个品牌&#xff09;在1979年发明的&#xff0c;是全球第一个真正用于工业现场的应用层总线协议。 为更好地普及和推动Modbus在基于以太网上的分布式应用&#xff0c;目前施…

[STM32-HAL库]ADC采集-DMA中断采集-平均值滤波-STM32CUBEMX开发-HAL库开发系列-主控STM32F103C8T6

目录 一、前言 二、实现步骤 1.STM32CUBEMX配置 2.Keil工程程序设计 三、结语 一、前言 本文通过STM32CUBEMX实现对ADC的数据采集和滤波操作&#xff0c;帮助各位开发者完成与模拟量输入的采集工作。 二、实现步骤 1.STM32CUBEMX配置 以STM32F103C8T6为例&#xff0c;打开S…

微信小程序上传包过大的最全解决方案!

微信小程序的发布大小限制是2MB。然而一个程序怎么能这么小&#xff1f; 介绍一下项目中的经验。 新项目 如果是刚开始做的新项目&#xff0c;一定确定好自己要用的Ui框架&#xff0c;而且确定之后&#xff0c;千万不要引入别的&#xff0c;否则占大小&#xff01;&#xff0…

6.Redis之String命令

1.String类型基本介绍 redis 所有的 key 都是字符串, value 的类型是存在差异的~~ 一般来说,redis 遇到乱码问题的概率更小~~ Redis 中的字符串,直接就是按照二进制数据的方式存储的. (不会做任何的编码转换【讲 mysql 的时候,知道 mysql 默认的字符集, 是拉丁文,插入中文…

sheng的学习笔记-AI-EM算法

AI学习笔记目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 目录 基础知识 什么是EM算法 EM算法简介 数学知识 极大似然估计 问题描述 用数学知识解决现实问题 最大似然函数估计值的求解步骤 Jensen不等式 定义 EM算法详解 问题描述 EM算法推导流程 EM算法流程…

java并发工具类都有哪些

Java中的并发工具类包括&#xff1a; CountDownLatch CountDownLatch允许一个或多个线程等待其他线程完成某些操作。它通常用于线程间的同步&#xff0c;例如在一个线程完成其工作后通知其他线程继续执行。 CyclicBarrier CyclicBarrier是一个同步辅助类&#xff0c;它允许一…

tomcat--安全配置多虚拟机

端口8005/tcp 安全配置管理 8005是Tomcat的管理端口&#xff0c;默认监听在127.0.0.1上。无需验证就可发送SHUTDOWN (大小写敏感)这个字符串&#xff0c;tomcat接收到后就会关闭此Server。此管理功能建议禁用&#xff0c;可将SHUTDOWN改为一串猜不出的字符串实现或者port修改成…