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

目录

1.面向过程与面向对象

1.1面向过程

1.2面向对象

1.3对比

2.类的引入

2.1类的声明方式

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

2.2.1单文件定义

2.2.2多文件定义

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

3.1访问限定符

3.2封装

4.类对象

4.1类对象的实例化

4.2类对象的存储

4.2.1 存储方式1

4.2.2 存储方式2

4.2.3存储方式3

4.3类对象的大小

4.3.1普通类对象的大小

 4.3.2空类的计算

5.this指针

5.1this指针的由来

5.2this指针的特点

5.3两个问题

5.3.1问题1

5.3.2问题2

 6.成员变量的命名


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.rhkb.cn/news/337094.html

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

相关文章

高并发项目-分布式Session解决方案

分布式Session解决方案 1.保存Session&#xff0c;进入商品列表页面 1.保存Session 1.编写工具类 1.MD5Util.java package com.sxs.seckill.utils;import org.apache.commons.codec.digest.DigestUtils;/*** Description: MD5加密工具类** Author sun* Create 2024/5/5 14…

Window系统安装Docker

因为docker只适合在liunx系统上运行&#xff0c;如果在window上安装的话&#xff0c;就需要开启window的虚拟化&#xff0c;打开控制面板&#xff0c;点击程序&#xff0c;在程序和功能中可以看到启动和关闭window功能&#xff0c;点开后&#xff0c;找到Hyper-V&#xff0c;Wi…

【环信IM集成教程】分分钟带你实现视频消息的在线播放和本地播放

有种需求&#xff0c;叫下班前实现 发送视频消息是即时通讯应用中很常见的功能&#xff0c;现在的视频播放场景五花八门&#xff0c;眼瞅快下班&#xff0c;接到产品需求 如何实现这个需求&#xff0c;好准点下班回家抢显卡 &#xff0c;快速提升自己的工作效率&#xff0c;那…

yolov5-ros模型结合zed2相机部署在 Ubuntu系统

前言 本篇文章主要讲解yolov5-ros模型结合zed2相机进行实时检测&#xff0c;经改进实现了红绿灯检测&#xff0c;并输出检测类别与置信度&#xff01; 目录 一、环境配置二、zed2驱动安装三、yolov5-ros功能包配置四、运行官方权重文件四、运行自己权重文件 一、环境配置 1、…

vue2转vue3初步下载pnpm遇到的问题 pnpm : 无法加载文件 D:\nodejs\pnpm.ps1

安装pnpm npm install -g pnpm pnpm -v 提示&#xff1a; 解决&#xff1a;nvm install 18.18.0 下载最稳定版本的nodejs nvm use 18.18.0 然后注意重新下载删除pnpm npm uninstall -g pnpm npm install -g pnpmlatest 在vscode使用pnpm报错 解决&#xff1a;管理员运行Windo…

博士毕业论文/CTEX/LATEX

LATEX环境安装 CTEX 安装 &#xff08;垃圾&#xff0c;不要装&#xff09; 运行 clean.batcomp.bat 缺少字体 Couldn’t find Adobe Heiti S.cfg’ miktex-maketfm: No creation rule for font “Adobe Heiti Std”.解决方法&#xff1a;其实就是下载这四个字体之后&…

深度学习-语言模型

深度学习-语言模型 统计语言模型神经网络语言模型语言模型的应用序列模型&#xff08;Sequence Model&#xff09;语言模型&#xff08;Language Model&#xff09;序列模型和语言模型的区别 语言模型&#xff08;Language Model&#xff09;是自然语言处理&#xff08;NLP&…

16:00面试,16:08就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

32. 【Java教程】集合

在前面的小节中&#xff0c;我们学习了数组&#xff0c;本小节学习的集合同样用于存放一组数据&#xff0c;我们将学习什么是集合、集合的应用场景 &#xff0c;在应用场景部分我们将对比 Java 数组与集合的区别&#xff0c;还将系统介绍 Java 集合的架构&#xff0c;也将结合实…

【课程总结】Day4:信息论和决策树算法

前言 本章内容主要是学习机器学习中的一个重要模型&#xff1a;决策树&#xff0c;围绕决策树的应用&#xff0c;我们展开了解到&#xff1a;熵的定义、熵的计算、决策树的构建过程(基于快速降熵)、基尼系数等&#xff0c;从而使得我们对决策树有了直观认识。 熵的介绍 因为…

用HAL库改写江科大的stm32入门-6-3 PWM驱动LED呼吸灯

接线图&#xff1a; 2 :实验目的&#xff1a; 利用pwm实现呼吸灯。 关键PWM定时器设置&#xff1a; 代码部分&#xff1a; int main(void) {/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*…

10.Halcon3D点云和MESH的相互转换

1.实现效果 这个案例主要是想告诉我们,如何在点云数据(全是点)和MESH(网格数据)中转换,理论上说可以点云数据可以看作的离散的,而MESH网格数据可以看作是连续的。 上图展示了三个(其实是四个)空间中的3d对象,左边第一个是一个立方体,经过降采样之后的点云,中间的是…

匿名函数(lambda)

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 匿名函数是指没有名字的函数&#xff0c;应用在需要一个函数&#xff0c;但是又不想费神去命名这个函数的场合。通常情况下&#xff0c;这样的函数只…

LabVIEW中进行步进电机的位置控制

在LabVIEW中进行步进电机的位置控制&#xff0c;通常涉及以下几个关键步骤&#xff1a;设置硬件、配置通信、编写控制算法和实施反馈控制。以下是一个详细的介绍。 硬件设置 步进电机&#xff1a;选择合适的步进电机&#xff0c;根据负载和应用需求选择适当的步数和转矩。 驱…

TensorFlow Playground神经网络演示工具使用方法详解

在现代机器学习领域,神经网络无疑是一个重要的研究方向。然而,对于许多初学者来说,神经网络的概念和实际操作可能显得相当复杂。幸运的是,TensorFlow Playground 提供了一个交互式的在线工具,使得我们可以直观地理解和实验神经网络的基本原理。在这篇博客中,我们将详细介…

IMU状态预积分代码实现 —— IMU状态预积分类

IMU状态预积分代码实现 —— IMU状态预积分类 实现IMU状态预积分类 实现IMU状态预积分类 首先&#xff0c;实现预积分自身的结构。一个预积分类应该存储一下数据&#xff1a; 预积分的观测量 △ R ~ i j , △ v ~ i j , △ p ~ i j \bigtriangleup \tilde{R} _{ij},\bigtrian…

Superset二次开发之更新 SECRET_KEY

SECRET_KEY 的作用 加密和签名:SECRET_KEY用于对敏感数据(如会话、cookie、CSRF令牌)进行加密和签名,防止数据被篡改。安全性:确保应用的安全性,防止跨站请求伪造(CSRF)攻击和会话劫持等安全问题。如何生成 SECRET_KEY openssl rand -base64 42 配置 SECRET_KEY 在sup…

git使用流程与规范

原文网址&#xff1a;git代码提交流程与规范-CSDN博客 简介 本文git提交流程与规范是宝贵靠谱的经验&#xff0c;它能解决如下问题&#xff1a; 分支差距过大&#xff0c;导致合代码无数的冲突合完代码后发现代码丢失分支不清晰&#xff0c;无法追溯问题合代码耗时很长&…

使用Spring Boot自定义注解 + AOP实现基于IP的接口限流和黑白名单

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

数据在内存中的存储<C语言>

导言 在计算机中不同类型的数据在计算机内部存储形式各不相同&#xff0c;弄懂各种数据在计算机内部存储形式是有必要的&#xff0c;C语言的学习不能浮于表面&#xff0c;更要锻炼我们的“内功”&#xff0c;将来在写程序的时候遇见各种稀奇古怪的bug时&#xff0c;也便能迎刃而…