初识【类和对象】
目录
1.面向过程和面向对象初步认识
2.类的引入
3.类的定义
4.类的访问限定符及封装
5.类的作用域
6.类的实例化
7.类的对象大小的计算
8.类成员函数的this指针
1.面向过程和面向对象初步认识
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
手洗衣服的过程:
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。实际上,在关注对象的时候,过程也就完成了,只是将注意力集中到对象身上,这里简单理解一下就好。什么是基于,是因为C++继承C语言,可以面向过程,也可以面向对象。
2.类的引入
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如:
之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,会发现struct中也可以定义函数:
struct Stack
{//成员函数,C语言结构体不能定义函数void Init()//可以不用传参数,因为在类里面,可以直接访问{a = nullptr;top = capacity = 0;}void Push(int x){if (top == capacity){int newcapacity = capacity == 0 ? 4 : capacity * 2;int* ptr = (int*)realloc(a, sizeof(int) * newcapacity);if (ptr == nullptr){printf("%s", strerror(errno));}capacity = newcapacity;a = ptr;}a[top] = x;top++;}//成员变量int* a;int top;int capacity;
};
上面结构体的定义,在C++中更喜欢用class来代替。
3.类的定义
class className
{// 类体:由成员函数和成员变量组成}; // 一定要注意后面的分号
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。下面的public,和 private是访问限定符,我们先不管它,后面会讲。
类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。
类的两种定义方式:
- 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,会默认为内联函数,内联函数有内部链接属性,当这个类被多个头文件包含时,不会造成函数重定义的问题。
- 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
一般情况下,更期望采用第二种方式。
成员变量命名规则的建议:
我们看看这个函数,是不是很僵硬:
上图局部优先,两个year都是函数参数year。
所以一般都建议这样:
或者这样:
其他方式也可以的,只要让成员变量和函数参数区别开就可以。
4.类的访问限定符及封装
4.1 访问限定符
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
【访问限定符说明】
- public修饰的成员在类外可以直接被访问
- protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的),在继承中有区别,我们后面文章会讲。
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- 如果后面没有访问限定符,作用域就到 } 即类结束。
- 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、显卡、内存等一些硬件元件。
在C++语言中实现封装:可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。
5.类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
class Person
{
public:void PrintPersonInfo();
private:char _name[20];char _gender[3];int _age;
};// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{cout << _name << " "<< _gender << " " << _age << endl;
}
6.类的实例化
用类类型创建对象的过程,称为类的实例化
- 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;
- 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
int main() {//Person只是一个类名,没有示例化对象Person._age = 100; // 编译失败:error C2059: 语法错误:“.”return 0; }
- 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对像才能实际存储数据,占用物理空间
7.类的对象大小的计算
7.1 如何计算类对象的大小
class A
{
public:void PrintA(){cout<<_a<<endl;}
private:char _a;
};
问题:类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算一个类的大小?
7.2 类和对象的存储方式猜测
- 对象中包含类的各个成员
缺陷:每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。那么如何解决呢?
- 代码只保存一份,在对象中保存存放代码的地址
- 只保存成员变量,成员函数存放在公共的代码段
问题:对于上述三种存储方式,那计算机到底是按照那种方式来存储的?
我们再通过对下面的不同对象分别获取大小来分析看下:
class A1
{
public:void f1(){}
private:int _a;
};
//类内仅有一个成员函数
class A2
{
public:void f2() {}
};
//空类
class A3
{};int main()
{cout << sizeof(A1) << endl;//4//占位 ,c++会分配1个字节 ,表示这个类存在,不存储数据,只是占位,表示对象存在过cout << sizeof(A2) << endl;//1cout << sizeof(A3) << endl;//1有没有成员函数大小是一样的,因为成员函数在公共代码区A2 aa1;//存储什么值不重要A2 aa2;cout << &aa1 << endl;//内存中有1个字节的空间cout << &aa2 << endl;return 0;
}
结论:一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐。
注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。
7.3 结构体内存对齐规则
1. 第一个成员在与结构体偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8
3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
【面试题】
1. 结构体怎么对齐? 为什么要进行内存对齐?
2. 如何让结构体按照指定的对齐参数进行对齐?能否按照3、4、5即任意字节对齐可以看我之前文章自定义数据类型:结构体,枚举,联合_西兰花也是花的博客-CSDN博客
8.类成员函数的this指针
8.1 this指针的引出
this 指针
class Date
{
public:void Init(int year, int month, int day){_year = year;//不是访问类内的成员,类内的只是声明,是访问调用对象的成员_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}int _year;int _month;int _day;
};int main()
{Date d1;d1.Init(2023, 8, 26);d1.Print();Date d2;d2.Init(2023, 8, 27);d2.Print();return 0;
}
对于上述类,有这样的一个问题:
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
//this 指针
class Date
{
public://this在实参和形参位置不能显示写//但是在类里面可以现实的用void Init(int year, int month, int day)//3个参数,还有一个隐藏的参数,this指针{cout << this << endl;_year = year;//不是访问类内的成员,类内的只是声明_month = month;_day = day;//this->_month = month;//this->_day = day;//没有写this编译器会自己加上//Print();//可以调用函数,前面也会自动加this->//this->Print();//成员变量和成员函数前都有this}void Print()//一个隐藏的参数,this指针{cout << this << endl;cout << _year << "/" << _month << "/" << _day << endl;}int _year;//只是声明,没有开辟空间int _month;int _day;
};
8.2 this指针的特性
- this指针的类型:类的类型 * const ,例如上面的Date* const this,即成员函数中,不能this指针赋值。
- 只能在“成员函数”的内部使用
- this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
- this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
【面试题】
1. this指针存在哪里?答:this指针是函数形参,存在栈区,但一般情况由编译器通过ecx寄存器传递。
2. this指针可以为空吗答:可以,但是调用的成员函数内不能访问对象的成员。看下面可以代码可以理解。
1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:void Print(){cout << "void Print()" << endl;}private:int _a;
};int main()
{A* p = nullptr;p->Print();return 0;
}
答案是C。因为没有对空指针解引用。
int main()
{A* p = nullptr;A p1;//看汇编 p->Print();//这里并没有解引用p,因为Print()函数在公共代码区,然后把p传递给this指针//传递this指针是直接放到寄存器中了,不是压栈传参//编译器很聪明,不会解引用,会转换成汇编指令,汇编与上面相同(*p).Print();//还是可以运行, 与上面同理,Print()没有在对象里面,不会访问对象p1.Print(); //这里会取p1的地址//编译器比较智能,老编译器可能会出错p->_a;//没有做任何事情,就没有生成指令,实际没有访问,其他编译器可能会报错return 0;
}
汇编代码:
看下面代码
class A
{
public:void Print(){cout << _a << endl;}private:int _a;
};int main()
{A* p = nullptr;p->Print();return 0;
}
这里就会出错,因为这里在成员函数内对空指针this进行了解引用。
8.3 C语言和C++实现Stack的对比
1. C语言实现
用C语言实现时,Stack相关操作函数有以下共性:
- 每个函数的第一个参数都是Stack*
- 函数中必须要对第一个参数检测,因为该参数可能会为NULL
- 函数中都是通过Stack*参数操作栈的
- 调用时必须传递Stack结构体变量的地址
结构体中只能定义存放数据的结构,操作数据的方法不能放在结构体中,即数据和操作数据的方式是分离开的,而且实现上相当复杂一点,涉及到大量指针操作,稍不注意可能就会出错
2. C++实现
C++中通过类可以将数据 以及 操作数据的方法进行完美结合,通过访问权限可以控制那些方法在类外可以被调用,即封装,在使用时就像使用自己的成员一样,更符合人类对一件事物的认知。而且每个方法不需要传递Stack*的参数了,编译器编译之后该参数会自动还原,即C++中 Stack *参数是编译器维护的,C语言中需用用户自己维护。
本篇结束。
相关文章:

初识【类和对象】
目录 1.面向过程和面向对象初步认识 2.类的引入 3.类的定义 4.类的访问限定符及封装 5.类的作用域 6.类的实例化 7.类的对象大小的计算 8.类成员函数的this指针 1.面向过程和面向对象初步认识 C语言是面向过程的,关注的是过程,分析出求解问题的…...

软考高级系统架构设计师系列论文八十六:论企业应用集成
软考高级系统架构设计师系列论文八十六:论企业应用集成 一、企业应用集成相关知识点二、摘要三、正文四、总结一、企业应用集成相关知识点 软考高级系统架构设计师系列之:企业集成平台技术的应用和架构设计二、摘要 2022年10月,我参加了***车站综合信息平台项目的开发,承…...

HarmonyOS ArkUI 属性动画入门详解
HarmonyOS ArkUI 属性动画入门详解 前言属性动画是什么?我们借助官方的话来说,我们自己简单归纳下 参数解释举个例子旋转动画 位移动画组合动画总结 前言 鸿蒙OS最近吹的很凶,赶紧卷一下。学习过程中发现很多人吐槽官方属性动画这一章比较敷…...

基于XGBoots预测A股大盘《上证指数》(代码+数据+一键可运行)
对AI炒股感兴趣的小伙伴可加WX:caihaihua057200(备注:学校/公司名字方向) 另外我还有些AI的应用可以一起研究(我一直开源代码) 1、引言 在这期内容中,我们回到AI预测股票,转而探索…...

5G NR:PRACH频域资源
PRACH在频域位置由IE RACH-ConfigGeneric中参数msg1-FrequencyStart和msg1-FDM所指示,其中, msg1-FrequencyStart确定PRACH occasion 0的RB其实位置相对于上行公共BWP的频域其实位置(即BWP 0)的偏移,即确定PRACH的频域起始位置msg1-FDM的取值…...

设计模式——组合模式
什么是组合模式 组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以…...

get属性是什么?有什么用?在什么场景用?get会被Json序列化?
在JavaScript中,对象的属性不仅可以是数据属性(即常规的键值对),还可以是访问器属性(accessor properties)。访问器属性不包含实际的数据值,而是定义了如何获取(get)和设…...

这可能是你看过最详细的 [八大排序算法]
排序算法 前置知识 [排序稳定性]一、直接插入排序二、希尔排序三、直接选择排序四、堆排序五、冒泡排序六、快速排序七、归并排序八、计数排序(非比较排序)排序复杂度和稳定性总结 前置知识 [排序稳定性] 假定在待排序的记录序列中,存在多个…...

docker的安装
CentOS7 安装 Docker 安装需要的软件包, yum-util 提供yum-config-manager功能,另两个是devicemapper驱动依赖 yum install -y yum-utils device-mapper-persistent-data lvm2 添加下载源 yum-config-manager --add-repo http://mirrors.aliyun.com/…...

【业务功能篇75】微服务项目环境搭建docker-mysql-redisSpringCloudAlibaba
项目环境准备 1.虚拟机环境 我们可以通过VMWare来安装,但是通过VMWare安装大家经常会碰到网络ip连接问题,为了减少额外的环境因素影响,Docker内容的讲解我们会通过VirtualBox结合Vagrant来安装虚拟机。 VirtualBox官网:https:/…...

学习笔记|认识数码管|控制原理|数码管实现0-9的显示|段码跟位码|STC32G单片机视频开发教程(冲哥)|第九集:数码管静态显示
文章目录 1.认识数码管2.控制原理十进制转换为任意进制其它进制转十进制 3.数码管实现0-9的显示1.用数组定义0-9的内码段码跟位码的区别2.尝试用延时实现0-9的循环显示3.用按键控制数字的加或者减。 总结课后练习: 1.认识数码管 数码管按段数可分为七段数码管和八段…...

CentOS 7/8 firewall 转发端口
#开启系统路由模式功能 echo net.ipv4.ip_forward1>>/etc/sysctl.conf sysctl -p #开启firewalld systemctl start firewalld 打开防火墙伪装IP # 检查是否允许伪装IP,返回no表示没开启,反之开启伪装IP firewall-cmd --query-masquerade #设置…...

mysql自动备份脚本
备份脚本 #!/bin/bash #author cheng #mysql数据自动备份 mysql_user“root” mysql_password“passwprd” mysql_host“localhost” mysql_port“3306” mysql_charset“utf8mb4” #备份文件存放地址(根据实际情况填写) backup_location/usr/cheng/msg_manager/sql #是否删…...

VUE笔记(九)vuex
一、vuex的简介 1、回顾组件之间的通讯 父组件向子组件通讯:通过props实现 子组件向父组件通讯:通过自定义事件($emit)方式来实现 兄弟组件之间的通讯:事件总线($eventBus)、订阅与发布方式来实现 跨级组件的通讯…...

Webpack高频面试题
Webpack高频面试题 1 谈谈你对webpack的看法 现在的前端网页功能丰富,特别是SPA(single page web application 单页应用)技术流行后,JavaScript的复杂度增加和需要一大堆依赖包,还需要解决Scss,Less……新…...

数字基带传输系统
文章目录 前言一、数字基带系统基本组成二、基本码型1、数字基带信号2、6 种基本码型 三、数字基带信号的频谱特性四、数字基带信号选码1、原则2、常用的传输码型①、AMI 码(传号交替反转码)②、 H D B 3 HDB_3 HDB3 码(3 阶高密度双极性码…...

FPGA使用MIG调用SODIMM内存条接口教程,提供vivado工程源码和技术支持
目录 1、前言免责声明 2、SODIMM内存条简介3、设计思路框架视频输入视频缓存MIG配置调用SODIMM内存条VGA时序视频输出 4、vivado工程详解5、上板调试验证6、福利:工程代码的获取 1、前言 FPGA应用中,数据缓存是一大重点,不管是图像处理还是A…...

深度学习数据预处理
参考文章:深度学习中的数据预处理方法总结 在深度学习中,数据预处理(preprocessing)的重要性体现在以下几个方面: 1、数据质量: 原始数据通常包含错误、缺失值、异常值和噪声。预处理能够检测和处理这些问…...

[C++] STL_vector 迭代器失效问题
文章目录 1、前言2、情况一:底层空间改变的操作3、情况二:指定位置元素的删除操作4、g编译器对迭代器失效检测4.1 扩容4.2 erase删除任意位置(非尾删)4.3 erase尾删 5、总结 1、前言 **迭代器的主要作用就是让算法能够不用关心底…...

C语言暑假刷题冲刺篇——day5
目录 一、选择题 二、编程题 🎈个人主页:库库的里昂 🎐CSDN新晋作者 🎉欢迎 👍点赞✍评论⭐收藏✨收录专栏:C语言每日一练✨相关专栏:代码小游戏、C语言初阶、C语言进阶🤝希望作者…...

若依Cloud集成Flowable6.7.2
项目简介 基于若依Cloud的Jove-Fast微服务项目,集成工作流flowable(接上篇文章) 若依Cloud集成积木报表 项目地址:https://gitee.com/wxjstudy/jove-fast 后端 新建模块 目录结构如下: 引入依赖 前提:引入依赖之前先配置好maven的setting.xml &…...

动态不确定性的动态S过程(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

LoadRunner操作教程
日升时奋斗,日落时自省 目录 1、Virtual User Generator (VUG) 1.1、WebTours系统 1.1.1、WebTours启动 1.1.2、WebTours配置 1.2、脚本录制 1.3、编译 1.4、脚本运行 1.5、加强脚本 1.5.1、事务插入 1.5.2、插入集合点 1.5.3、参…...

.NET Core 实现日志打印输出在控制台应用程序中
在本文中,我们将探讨如何在 .NET Core 应用程序中将日志消息输出到控制台,从而更好地了解应用程序的运行状况。 .NET Core 实现日志打印输出在控制台应用程序中 在 .NET Core 中,日志输出打印是使用 Microsoft.Extensions.Logging 命名空间…...

Nginx正向代理与反向代理及Minio反向代理实操(三)
本文是对: Nginx安装及Minio集群反向动态代理配置(二) 文的进一步完善: 多台服务器间免密登录|免密拷贝 Cenos7 搭建Minio集群部署服务器(一) Cenos7 搭建Minio集群Nginx统一访问入口|反向动态代理(二) Spring Boot 与Minio整合实现文件上传与下载(三) CentOS7的journa…...

Xmake v2.8.2 发布,官方包仓库数量突破 1k
Xmake 是一个基于 Lua 的轻量级跨平台构建工具。 它非常的轻量,没有任何依赖,因为它内置了 Lua 运行时。 它使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt,配置语法更加简洁直观,对新手非常友好&#x…...

加油站抽烟烟火智能识别算法
加油站抽烟烟火智能识别系统通过yoloopencv网络模型图像识别分析技术,加油站抽烟烟火智能识别算法识别出抽烟和燃放烟火的情况,并发出预警信号以提醒相关人员,减少火灾风险。OpenCV基于C实现,同时提供python, Ruby, Matlab等语言的…...

web前端开发中的响应式布局设计是什么意思?
响应式布局是指网页设计和开发中的一种技术方法,旨在使网页能够在不同大小的屏幕和设备上都能良好地显示和交互。这种方法使得网页可以自动适应不同的屏幕尺寸,包括桌面电脑、平板电脑和手机等。 在Web前端开发中,响应式布局通常使用CSS&…...

【LeetCode-面试经典150题-day14】
目录 19.删除链表的倒数第N个结点 82.删除排序链表中的重复元素Ⅱ 61. 旋转链表 86.分隔链表 146.LRU缓存 19.删除链表的倒数第N个结点 题意: 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 【输入样例】head [1,2,3,4,5…...

【算法系列总结之分组循环篇】
【算法系列总结之分组循环篇】 分组循环1446.连续字符1869.哪种连续子字符串更长1957.删除字符使字符串变好2038.如果相邻两个颜色均相同则删除当前颜色1759.统计同质子字符串的数目2110.股票平滑下跌阶段的数目1578.使绳子变成彩色的最短时间1839.所有元音按顺序排布的最长子字…...