谈对象系列:C++类和对象
文章目录
- 一、类的定义
- 1.1类定义的格式
- 类的两种定义方法
- 结构体:
- 1.2访问限定符
- 1.3类域
- 二、实例化
- 2.1变量的声明和定义
- 2.2类的大小
- 计算空类的大小(面试):
- 三、this指针
- 小考题
一、类的定义
1.1类定义的格式
使用class关键字,定义类,calssName是类名,{}中为类的主体,最后的分号 ;可别忘了加上。
类的名称,就是类的类型。
- 类定义的函数,属于inline内联函数,但具体展开还是得看编译器的选择。
class calssName
{//成员函数 //成员变量
};
int main()
{tag st1;//类名,就是类型
}
成员变量(member)的特殊标识:
- 在变量名前加上下划线
- 在变慢名前加上字母m
类的两种定义方法
将函数的定义和声明放在类里面实现:
这里使用class定义了一个日期类,在main函数里声明了一个day1对象,使用点操作符(.)、箭头操作符 (->)来访问类成员函数。
#include <iostream>
using std::cout;
using std::endl;class Data
{
public:void Init(int year = 2024, int month = 6, int day = 6){_year = year;_month = month;_day = day;}void print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Data day1;day1.Init(2024, 5, 20); day1.print();return 0;
}
将函数声明放在类里面,函数定义在类外面实现。
stack.h
#include <iostream>
class Stack
{
public:void Init(int capacity = 4);void Push(int x);void Pop();int Top();int Empty();void Destry(){assert(_next);if (_next)free(_next);_next = NULL;_top = 0;_capacity = 0;}
private:int _top;int _capacity;int* _next;
};
stack.cpp
void Stack::Init(int capacity)
{_next = (int*)malloc(sizeof(int) * capacity);_top = 0;_capacity = capacity;
}
void Stack::Push(int x)
{if (_top == _capacity){int newcapacity = _capacity == 0 ? 4 : _capacity * 2;int* newNext = (int*)realloc(_next, sizeof(int) * newcapacity);if (newNext == NULL){perror("realloc fail");exit(1);}_next = newNext;_capacity = newcapacity;}_next[_top++] = x;
}void Stack::Pop()
{assert(_next && _top > 0);_top--;
}
int Stack::Top()
{assert(_next && _top > 0);return _next[_top - 1];
}
int Stack::Empty()
{return _top == 0;
}
结构体:
在C++中兼容C语言,C++编译器也支持struct关键字,满足结构体的功能C,不仅如此C++还对它进行了升级,也支持类的定义格式。
这里在C语言中定义一个链表的结构的。
struct ListNode
{int val;struct ListNode* next;
};
而C++兼容C语言,又对结构进行升级,在定义链表时可以直接省略struct关键字。
struct ListNode
{int val;ListNode* next;
};
在结构体里和类一样还可以定义函数。下面使用C++实现栈。
struct Stack
{
public:void Init(int capacity = 4){_next = (int*)malloc(sizeof(int) * capacity);_top = 0;_capacity = capacity;}void Push(int x){if (_top == _capacity){int newcapacity = _capacity == 0 ? 4 : _capacity * 2;int* newNext = (int*)realloc(_next, sizeof(int) * newcapacity);if (newNext == NULL){perror("realloc fail");exit(1);}_next = newNext;_capacity = newcapacity;}_next[_top++] = x;}void Pop(){assert(_next && _top > 0);_top--;}int Top(){assert(_next && _top > 0);return _next[_top - 1];}int Empty(){return _top == 0;}void Destry(){assert(_next);if (_next)free(_next);_next = NULL;_top = 0;_capacity = 0;}
private:int _top;int _capacity;int* _next;
};
int main()
{Stack s1;s1.Init();s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);while (!s1.Empty()){cout << s1.Top() << endl;s1.Pop();}s1.Destry();return 0;
}
-
可以发现使用C++实现的栈,与C语言没有过多的区别,只是将栈的接口放在了结构体内部。
-
而在使用栈的结构体时,并不像C语言那样需要频繁的传地址,只需调用类成员函数即可,以及在每一个函数名前省略了Stack,栈的函数是在结构体内实现的,属于Stack类域,即使是其它地方的函数实现了,Init、Push、Pop等功能,那也是支持的。
-
调用栈的接口函数需使用 点操作符
(.)。
1.2访问限定符
可以发现,在上述实现类中,使用了 pulic、private这两个是访问限定符,用于限定对类成员访问的权限
- C++将对象的属性与方法封装在一起,通过访问权限选择性的将接口提供给外部使用。
public公有的,被public修饰的成员在类外可以被访问,private和protected修饰的成员不能在类外部直接访问,它两的效果差不多,常用private,两者之间的区别要在继承中才能体现出。- 访问限定符的作用域,从限定符开始知道遇见下一个限定符结束,或者遇见
}结束。 - class定义的成员没有使用访问限定符修饰,class内默认使用
private修饰,struct内默认使用pulic修饰。- 被限制在了类中,而不能在外部使用相当于一种保护,是不希望类中被🔒锁上的成员被修改。一般不提供外部使用的成员变量使用
private和protected修饰,提供外部使用的成员函数使用pulic修饰。
- 被限制在了类中,而不能在外部使用相当于一种保护,是不希望类中被🔒锁上的成员被修改。一般不提供外部使用的成员变量使用
1.3类域
类定义了一个新的作用域,类域影响了编译器的查找规则,类的所有成员都在类域中,想要访问类域的成员使用
::域作用限定符指出成员属于哪个类。
如同上述的将函数声明放在类里面,函数定义在类外面实现。想要在类外实现类函数的定义,就必须使用上::域作用限定符
使用 Stack::,Stack指明了Init函数属于Stack类域
void Stack::Init(int capacity)
{_next = (int*)malloc(sizeof(int) * capacity);_top = 0;_capacity = capacity;
}
声明和定义分离,需要指定类域
二、实例化
2.1变量的声明和定义
变量的声明是不占空间内存的,只是告诉编译器存在这么个东西;而定义是在内存中开辟空间。
以下变量是声明还是定义?
class Data
{int _year;//声明int _month;//声明int _day;//声明
};
int val;//定义
int main()
{int n;//定义return 0;
}
声明就相当于别人给你的口头承诺,至于会不会实现那是另一回事,只是他告诉你有这么个事,而定义就相当于别人实实在在的帮你做完了事,不局限于声明。
对象的实例化:
对象实例化,就是使用类类型创建变量的过程,类的成员变量只是一个声明,还没有为其开辟空间,类是对对象的一个描述,需要那些功能,那些变量实现一个对象,就会在类中一一实现。
就好比如一个塑料瓶子,有了一个瓶子的模型,需要大量生成塑料瓶的时候都会按照这个模型生产。类和对象也如同这样,使用类将一个需要描述的对象的功能,框架等一一在类中实现,在需要使用的时候使用类创建一个对象。
同理一个类可以实例化多个对象,就像一个冰棍的模型可以生产许多颗冰棍。实例化的对象会占据实际的物理空间,存储数据。
2.2类的大小
该如何去计算一个类的大小,它实际上与之前学习过的计算结构体大小规则一致。
结构体内存对齐的原则:
-
结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处(上述两个结构体c1偏移量为0的原因)
-
其它成员变量要对齐到对齐数的整数倍的地址处。
- 对齐数:编译器默认的第一个对齐数 与 该成员变量大小的较小值。
- vs中默认—8
- linux中gcc 没有默认对齐数,对齐数就是成员自身大小。
-
结构体中每个成员变量都有一个对齐数。
-
结构体总大小为最大对齐数(结构体中最大对齐数)的整数倍。
-
如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员中的对齐数)的整数倍。
那,咱就使用对齐规则尝试计算类的大小
class Data
{int _year;int _month;int _day;
};

可以发现,使用对齐规则计算后的大小为12个字节。
但类中是有函数的定义,计算类的大小时需要将类成员函数也一起计算吗?事实上并不需要。
#include <iostream>
using std::cout;
using std::endl;class Data
{
public:void Init(int year = 2024, int month = 6, int day = 6){_year = year;_month = month;_day = day;}void print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{printf("%zd\n", sizeof(Data));return 0;
}

可以发现,在类中定义了两个类成员函数后,计算的大小还是12个字节,符合上述通过对齐规则计算后的结果。
类没有为类成员函数开辟内存空间,那它们是存储在哪里。
类成员函数与类成员变量分开存储的。在计算类的大小是只考虑成员变量。使用同一个类实例化的对象,对应每一个对象的成员变量来说都是存放在不同的空间中来管理数据,而每一个对象的成员函数 例如Init,都需调用相同的函数,没有必要为每一个对象都开辟空间存放 Init函数,这样会造成大量的空间浪费。
成员函数都有一块公共区域来存放,对于每一个对象来说是公有的,大家可以一块使用。
计算空类的大小(面试):
class S{};
计算一个空类,它的大小肯定不会为0
int main()
{S s;printf("%zd\n", sizeof(s));return 0;
}

在main函数里定义了类S对象s,它是通过类的大小开辟空间,若一个空类的大小为0,定义空类的时候编译器就会报错。
编译器给空类一个字节类唯一标识这个类的对象,这一个字节不存储有效数据,为一个占位符,标识对象被实例化。
三、this指针
Date类中有Init和print两个成员函数,函数体内没有为不同对象进行区别,在使用day1、day2调用
Init函数时,函数是如何区别是day1还是day2的。这里就通过C++提供的隐含的this指针来解决。
- 编译器编译后,类的成员函数默认都会在形参的第一个位置,增加一个当前类类型的指针,叫this指针。Date类中实现的
Init函数原型为void Init(Data* const this, int year int month, int day) - this作为一个关键字存在,不能显示的写在形参中,编译器会自动完成。规定
- 在函数内可以显示的用this指针,可以给它加上,也可以不加。不添加并不代表不存在this指针
- 外界无法传入当前对象的地址给到被调用的成员函数,使用this指针接受当前对象的地址,,this指针就会通过不同的地址去找到内存中对应的成员变量。
- this指针只能在成员函数内部使用,因为它是作为一个成员函数的形参,若是没有传递给当前对象地址的话,那么它的指向是不确定的。
- this指针作为函数参数,是不会存储在类中,而是随着函数栈帧的开辟,存放在栈区。
class Date
{
public://void Init(Data* const this, int year, int month, int day)void Init(int year, int month, int day){_year = year;_month = month;_day = day;//this->_year = year;//this->_month = month;//this->_day = day;}void print(){cout << _year << "/" << _month << "/" << _day << endl;}private:int _year;int _month;int _day;
};
int main()
{Date day1;Date day2;day1.Init(2024, 5, 20); day1.print();return 0;
}


通过编译器调试,可以发现的确存在this指针来接收了day1传递的地址。
小考题
下面程序编译运行后的结果是:
A、编译报错 B、运行崩溃 C、正常运行
#include<iostream>
using namespace std;
class A
{
public:void Print(){cout << "A::Print()" << endl;cout << _a << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();return 0;
}
答案是B,主函数内部不存在空指针的解引用,Print不存在对象里面,是在一块公共区域,若 p->_a这样写,就会出现空指针的解引用, _a是存在对象内部的,就像结构体里空链表对结构体成员的访问。
- 成员函数的指针是在编译时确定的,没有存放在对象中,虽然写成了
p->Print(),但是没有解引用。
问题出在Print函数内,Print函数的this指针接收了传递过来的空指针,而又使用 this->_a,对空指针进行了解引用,从而造成程序崩溃。
相关文章:
谈对象系列:C++类和对象
文章目录 一、类的定义1.1类定义的格式类的两种定义方法结构体: 1.2访问限定符1.3类域 二、实例化2.1变量的声明和定义2.2类的大小计算空类的大小(面试): 三、this指针小考题 一、类的定义 1.1类定义的格式 使用class关键字&…...
设计模式20-备忘录模式
设计模式20-备忘录 动机定义与结构定义结构 C代码推导优缺点应用场景总结备忘录模式和序列化备忘录模式1. **动机**2. **实现方式**3. **应用场景**4. **优点**5. **缺点** 序列化1. **动机**2. **实现方式**3. **应用场景**4. **优点**5. **缺点** 对比总结 动机 在软件构建过…...
绘制echarts-liquidfill水球图
文章目录 一、效果图二、步骤1.安装插件2.引入2.主要代码2.素材图片 总结 一、效果图 二、步骤 1.安装插件 npm install echarts npm install echarts-liquidfillecharts5的版本与echarts-liquidfill3兼容,echarts4的版本与echarts-liquidfill2兼容,安装的时候需要…...
应急响应:D盾的简单使用.
什么是应急响应. 一个组织为了 应对 各种网络安全 意外事件 的发生 所做的准备 以及在 事件发生后 所采取的措施 。说白了就是别人攻击你了,你怎么把这个攻击还原,看看别人是怎么攻击的,然后你如何去处理,这就是应急响应。 D盾功…...
c语言第14天笔记
通过指针引用数组 数组元素的指针 数组指针:数组中的第一个元素的地址,也就是数组的首地址。 指针数组:用来存放数组元素地址的数组,称之为指针数组。 注意:虽然我们定义了一个指针变量接收了数组地址,但…...
服装行业QMS中的来料检验:常见问题解析与解决策略
在服装行业的来料检验过程中,常会遇到一系列问题,这些问题可能影响到原材料的质量,进而影响最终产品的品质。以下将详细介绍来料检验的常见问题及相应的解决方法: 一、常见问题 外观瑕疵 问题描述:原材料表面存在污渍…...
健身动作AI识别,仰卧起坐计数(含UI界面)
用Python和Mediapipe打造,让你的运动效果一目了然! 【技术揭秘】 利用Mediapipe的人体姿态估计,实时捕捉关键点,精确识别动作。 每一帧的关键点坐标和角度都被详细记录,为动作分析提供数据支持。 支持自定义动作训练&a…...
GitHub开源金融系统:Actual
Actual:电子金融,本地优先,自由开源- 精选真开源,释放新价值。 概览 Actual的创新之处在于其对个人财务管理的全面考虑,它不仅仅是一个简单的记账工具,而是一个综合性的理财解决方案。它的本地优先设计意味…...
【学习笔记】Day 7
一、进度概述 1、DL-FWI基础入门培训笔记 2、inversionnet_train 试运行——未成功 二、详情 1、InversionNet: 深度学习实现的反演 InversionNet构建了一个具有编码器-解码器结构的卷积神经网络,以模拟地震数据与地下速度结构的对应关系。 (一…...
网络中特殊的 IP 地址
特殊网络 IP 127.0.0.1 127.0.0.1 是本机回送地址,发送到 127.0.0.1 的数据或者从 127.0.0.1 返回的数据只会在本机进行传输, 而不进行外部网络传输。 主要有以下两个作用: 测试本机网络 当我们可以 ping 通 127.0.0.1 的时候, 则说明本机的网卡以及 tc…...
ASP 表单处理入门指南
ASP 表单处理入门指南 简介 ASP(Active Server Pages)是一种由微软开发的服务器端脚本环境,用于动态生成交互性网页。它允许开发者结合HTML、VBScript或JScript脚本语言来创建和运行动态网页或Web应用程序。本文将重点介绍如何使用ASP来处理表单数据,包括表单的创建、数据…...
极米RS10Plus性价比高吗?7款4-6K价位投影仪测评哪款最好
通常家庭想买个投影仪都会选择4-6K这个价位段的投影仪,3K以下的投影配置太低,6K以上的价格略高,4-6K价位段的中高端投影仪正好满足大部分家庭的使用需求。正好极米投影在8月份上新了一款Plus版本的长焦投影:极米RS10Plusÿ…...
RocketMQ怎么对文件进行读写的?
RocketMQ 对文件的读写主要依赖于其底层的存储机制,核心组件是 CommitLog 和 ConsumeQueue,并且通过 MappedFile 类来进行高效的文件操作。以下是 RocketMQ 文件读写的详细介绍: 1. CommitLog CommitLog 是 RocketMQ 的核心存储文件&#x…...
智慧宠物护理:智能听诊器引领健康监测新潮流
在宠物健康科技的浪潮中,智能听诊器的诞生标志着宠物健康管理迈向了智能化的新纪元。广州坎普利智能信息科技有限公司的创新产品,正为宠物主人和他们的毛茸茸伙伴带来前所未有的关怀体验。 创新特点 这款智能听诊器,以其前沿科技和人性化设…...
SRE工程师第2天:我只要截图功能 而不是打开微信
大家好,我是watchpoints 别想太多,只管去提问,所有问题,都会有答案 watchpoints是我github用户名 , 也是我的wechat 用户名,如果我有讲不明白 欢迎提问 什么是SRE(Site Reliability Engineer) 和…...
【RunnerGo】离线安装成功版本
目录 一、下载 二、解压安装包 三、修改安装配置 3.1 编辑修改安装参数(我没有改,默认安装即可) 3.2 安装目录结构说明 四、执行安装 五、检查服务并使用 六、访问 前言:最近在调研一个新工具,发现RunnerGo&…...
AI 手机的技术展望
某某领导问到我,AI手机这个产业发展如何?对于,地方科技园区,应该如何发展相关产业?我一时还真说不上来,于是,查了一下资料,大概应对了一下。 一:AI手机的定义 首先&…...
实战 Springboot2 集成Redis 哨兵模式、集群模式、缓存管理、Lettuce拓扑刷新
redis搭建集群模式、Cluster模式(6节点,3主3从集群模式,添加删除节点)_redis cluster节点带数据增减-CSDN博客 Linux部署Redis哨兵集群 一主两从三哨兵(这里使用Redis6,其它版本类似)_linux red…...
MYSQL--binlog和redo log
前言 MySQL日志 MySQL日志主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。其中比较重要的就是二进制日志binlog(归档日志)、事务日志redo log(重做日志)和undo log(回滚日志)。 这篇…...
R语言医疗数据分析笔记
分组因子又是什么意思,分组因子和数组的区别是什么 举个实际的例子 分组因子 分组因子是分类变量,用于将数据分成不同组以便于比较或分析。例如,在一项研究中,研究对象的性别(男性和女性)可以视为一个分组…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
