【C++学习】内联函数 | nullptr空指针 | 初步认识面向对象 | 类访问限定符 | 封装 | 类对象的内存对齐

news2024/11/8 19:34:45

写在前面:

上一篇文章我介绍了引用和auto相关的知识,

如果有兴趣的话可以去看看:http://t.csdn.cn/j6jsI

这篇文章大概能够讲完C++入门的一些语法,开始类和对象的学习之旅。

目录

写在前面:

1. 内联函数

2. nullptr空指针

3. 初步认识面向对象

4. 类的引入

5. 类访问限定符

6. 封装

7. 类对象的内存对齐

写在最后:


1. 内联函数

我们先来看这样一个情况:

#include <iostream>
using namespace std;

int Add(int x, int y) {
	return x + y;
}

int main()
{
	for (int i = 0; i < 10000; i++) {
		cout << Add(i, i + 1) << endl;
	}
	return 0;
}

这段代码循环调用了一万次这一个Add函数,

创建销毁了一万次这个函数的函数栈帧,造成了大量的资源消耗,

我们该怎么解决这样的问题呢?

在学习C语言的时候,我们一般使用宏函数来解决这样的问题,

来看代码:

#include <iostream>
using namespace std;

#define Add(x, y) ((x) + (y))

int main()
{
	for (int i = 0; i < 10000; i++) {
		cout << Add(i, i + 1) << endl;
	}
	return 0;
}

使用宏函数本质上是一种替换,

将 Add ( i, i + 1 ) 替换成 ( ( i ) + ( i + 1 ) )

宏函数有他的优点也有缺点,

优点:不需要建立栈帧,提高调用效率;

缺点:复杂,容易出错,可读性差,不能调试,没有类型安全的检查。

C++就想着新增一种方法来解决这样的问题,减少宏函数的缺点,也就是内联函数。

内联函数的用法很简单,就是在函数前面加一个关键字:inline

#include <iostream>
using namespace std;

inline int Add(int x, int y) {
	return x + y;
}

int main()
{
	for (int i = 0; i < 10000; i++) {
		cout << Add(i, i + 1) << endl;
	}
	return 0;
}

加上inline 之后函数就会变成内联函数,

内联函数会在调用的地方展开,这样就没有函数调用了,

这就是内联函数,他不需要建立栈帧,提高了效率,

不复杂,不容易出错,可读性强,可以调试,几乎是一招解决了所有问题。

那你可能会问,如果内联函数这么牛逼,我们能不能把所有函数都搞成内联呢?

但是宏函数和内联函数都有一个适用场景,

他们适用于短小的频繁调用的函数,太长的是不适合的,会导致代码膨胀的问题。

实际上,内联仅仅只是一个建议,最终是否是内联,是编译器自己决定的。

一般而言,比较长的函数是不会成为内联的,一般递归也不会成为内联。

另外,在默认的debug模式下,一般是不支持内联的。

补充:如果要用内联,就别搞声明和定义分离,直接写就行了。

2. nullptr空指针

C语言已经有NULL了,为什么C++还要添加nullptr呢?

来看这段代码:

#include <iostream>
using namespace std;

void f(int x) {
	cout << "f(int x)" << endl;
}

void f(int* x) {
	cout << "f(int* x)" << endl;
}

int main()
{
	f(0);
	f(NULL);

	return 0;
}

NULL代表的是空指针,我们用NULL想调用第二个函数,但是,

这段代码的输出:

f(int x)
f(int x)

为什么会出现这样的情况?

我们可以看看NULL的底层是怎么样的:

他实际上就是宏定义出来的0,

我们来看nullptr:

#include <iostream>
using namespace std;

void f(int x) {
	cout << "f(int x)" << endl;
}

void f(int* x) {
	cout << "f(int* x)" << endl;
}

int main()
{
	f(0);
	f(NULL);
	f(nullptr); // #define nullptr ((void*)0)

	return 0;
}

 输出:

f(int x)
f(int x)
f(int* x)

他调用的就是第二个函数,

为什么呢?

其实就是因为nullptr的类型是 void*,算是给NULL打的一个补丁,

所以我们以后一般尽量都使用nullptr就行。

3. 初步认识面向对象

用一个经典的例子来解释面向对象和我们之前学习的面向过程的区别:

比如说一个外卖系统:

如果是用面向过程的思想解决:

就可以分成几个步骤:上架,点餐,派单,送餐等等。

如果是用面向对象的思想解决:

就可以分成几个对象:平台,商家,骑手,用户等等。

面向对象的优势是可以在同一个抽象的系统中实例化出多个对象,

关注的是对象和对象之间的关系和交互。

这些听起来很抽象,慢慢理解就行。

4. 类的引入

其实我们之前C语言就有结构体这一种自定义类型,

到了C++,结构体就被升级成了类,来看例子:

#include <iostream>
using namespace std;

//并且在C/C++里面用{}括起来的位置都是一个域,这里是就是类域
struct Stack {
	//成员函数(类内可以放成员函数)
	void Init() {
		//...
	}

	void Push() {
		//...
	}

	//...等等

	//成员变量
	int* a;
	int top;
	int capacity;
};

// C++兼容C语言,struct以前的用法都能继续用
// 而struct升级成了类,类的类名能直接当类型使用
int main()
{
	struct Stack st1;
	Stack st2;

	//调用的时候就可以这样调用
	st2.Init();
	st2.Push();

	return 0;
}

其实C++更喜欢使用的是class,也就类,class和struct基本上没什么区别,

当我们把struct改成class之后:

发现编译器报错了,这是为什么?

这里就要说到另一个知识点。

5. 类访问限定符

C++给出了三种访问限定符:

public(公有)

private(私有)

protected(保护)

而公有表示的是能在类外面访问,私有和保护表示的是不能在类外面访问。

私有和保护在平时的使用上是一样的,只有在继承的时候有所区别。

这个时候就能回答为什么改成class之后编译器会报错了,

因为struct类域默认是公有,而class的类域默认是私有。

(这个设计其实就是为了兼容C语言)

那么回归正题,平时我们在定义的类时候,

我们习惯将成员变量放在private私有,将成员函数放在public公有。

说人话就是:我想给你用的就放公有,不想让你碰到的就放在私有:

#include <iostream>
using namespace std;

//并且在C/C++里面用{}括起来的位置都是一个域,这里是就是类域
class Stack {
public:
	//成员函数(类内可以放成员函数)
	void Init() {
		//...
	}

	void Push() {
		//...
	}

	//...等等
		 
private:
	//成员变量
	int* a;
	int top;
	int capacity;
};

// C++兼容C语言,struct以前的用法都能继续用
// 而struct升级成了类,类的类名能直接当类型使用
int main()
{
	struct Stack st1;
	Stack st2;

	//调用的时候就可以这样调用
	st2.Init();
	st2.Push();

	return 0;
}

这个时候又出现了新的问题,

来看下面这段代码:

#include <iostream>
using namespace std;

class Date {
public:
	void Init(int year) {
		year = year;
	}

private:
	int year;
};

int main()
{
	Date d;
	d.Init(2023);

	return 0;
}

这段代码中 Init 函数里面的 year = year ,你知道是谁赋值给谁吗?

编译器并没有报错,跑过了,

这里我再讲的清楚一点,这两个year究竟是成员变量还是函数形参?

因为这样的原因,我们一般习惯给成员函数加一点标记:

#include <iostream>
using namespace std;

class Date {
public:
	void Init(int year) {
		_year = year;
	}

private:
	int _year;
};

int main()
{
	Date d;
	d.Init(2023);

	return 0;
}

 给成员函数前面加上一个_ ,我习惯这样区分,其实还有其他的区分方式,

每个人又不一样的代码风格,这个就看个人喜好或者是其他的需求了。

这里是没有硬性的要求的。

6. 封装

其实我们将给别人用的成员函数放在公有,

把成员变量放在私有,其实这就是封装思想的一种体现。

封装是什么?

将数据和数据的方法有机结合,隐藏对象的属性和实现细节,

仅对外公开接口来和对象进行交户的行为就是封装。

封装的本质其实是一种更好的管理。

这里补充一句:类内的成员函数默认都是内联函数。

7. 类对象的内存对齐

我们在学习C语言结构体的时候,曾经学过结构体的内存对齐,

那么下面这个类的内存对齐是多少呢?  

#include <iostream>
using namespace std;

class Stack {
public:
	void Init() {
		//...
	}

	void Push() {
		//...
	}

	//...等等
		 
private:
	//成员变量
	int* a;
	int top;
	int capacity;
}; 

int main()
{
	Stack st1;
	cout << sizeof(st1) << endl;

	return 0;
}

输出:

12

是的,你没有看错,类的内存对齐计算方法是跟结构体一模一样的,

而且,类的成员函数是不被计算在内的。

这个时候你可能会有疑问,为什么类的成员函数没有被计算在内?

来看这样一个例子:

#include <iostream>
using namespace std;

class Stack {
public:
	void Init() {
		//...
	}

	void Push() {
		//...
	}

	//...等等
		 
//private:
	//成员变量
	int* a;
	int top;
	int capacity;
}; 

int main()
{
	Stack st1;
	st1.top = 0;
	st1.Init();

	Stack st2;
	st2.top = 10;
	st2.Init();

	return 0;
}

我将类内成员的访问限定设置成公有,

那么 st1 的成员变量 top 跟 st2 的成员变量 top 是同一个变量吗?

显然不是,他们有着各自独立的空间,存放着不同的数据,

那么,st1 调用的 Init 函数和 st2 调用的 Init 函数他们调用的是同一个函数吗?

实际上,他们调用的就是同一个函数,

不然要是每实例化一个新的对象就要拷贝一份成员函数,那浪费的资源可太多了。

那问题来了,这个函数他存放在哪里呢?为什么两个对象都能调用的到?

这个问题就由我下一篇文章再来揭晓了。

写在最后:

以上就是本篇文章的内容了,感谢你的阅读。

如果感到有所收获的话可以给博主点一个哦。

如果文章内容有遗漏或者错误的地方欢迎私信博主或者在评论区指出~

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

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

相关文章

用Python带你制作小时候玩的“大富翁”(文末赠书)

名字&#xff1a;阿玥的小东东 学习&#xff1a;Python、C/C 主页链接&#xff1a;阿玥的小东东的博客_CSDN博客-python&&c高级知识,过年必备,C/C知识讲解领域博主 目录 首先 接下来需要定义各种类型的物业&#xff0c;包括普通物业、铁路、公用事业等等。 接下来需…

【Spring 】项目创建和使用

哈喽&#xff0c;哈喽&#xff0c;大家好~ 我是你们的老朋友&#xff1a;保护小周ღ 谈起Java 圈子里的框架&#xff0c;最年长最耀眼的莫过于 Spring 框架啦&#xff0c;如今已成为最流行、最广泛使用的Java开发框架之一。不知道大家有没有在使用 Spring 框架的时候思考过这…

强化学习从基础到进阶-常见问题和面试必知必答[5]::梯度策略、添加基线(baseline)、优势函数、动作分配合适的分数(credit)

【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧&#xff08;调参、画图等、趣味项目实现、学术应用项目实现 专栏详细介绍&#xff1a;【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧…

Spring Cloud - Nacos 注册发现、分级模型、配置集群、环境隔离、原理

目录 一、Nacos 安装和配置 二、Nacos 服务注册发现 2.1、将服务注册到 nacos 中 2.2、执行效果 三、Nacos 的服务分级模型及配置 3.1、分级模型 3.2、配置集群 3.3、配置 Nacos 负载均衡策略 3.4、Nacos 服务实例的权重设置 3.5、环境隔离——namespace 四、Nacos注…

真实企业做自动化测试做法,从测试用例到测试报告...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 首先&#xff0c;…

SAP MM 组织结构配置

1.定义公司代码 创建公司代码一般来说是财务去配置&#xff0c;但是我们需要去有一个基本的了解&#xff0c;这样&#xff0c;你才能理清公司代码和MM 组织的关系 配置&#xff1a;SAP IMG Enterprise Structure -》Definition -> Financial Accounting -> Edit, Copy, …

深度学习(26)——YOLO系列(5)

深度学习&#xff08;26&#xff09;——YOLO-v7&#xff08;5&#xff09; 文章目录 深度学习&#xff08;26&#xff09;——YOLO-v7&#xff08;5&#xff09;絮絮叨叨1. conv和BN的融合2. 3*3卷积的替换&#xff08;1&#xff09;1*1卷积有什么作用&#xff1f;&#xff08…

解决方案 | 照明行业数字化营销CRM平台

“数字中国”作为二十大报告的“关键词”&#xff0c;也成为各行各业的高质量发展的主旋律&#xff0c;历史悠久的中国照明产业也积极拥抱“数字化”&#xff0c;以驱动高质量发展 。作为照明行业的领军企业&#xff0c;某照明行业客户很早就意识到企业数字化转型的重要性&…

Spring事务源码详解-spring原码(二)

上篇文章介绍了事务开启&#xff0c;前面介绍了解析adviors。 spring事务源码详解-spring原码&#xff08;一&#xff09;https://blog.csdn.net/ke1ying/article/details/131360060 事务源码 先从缓存里获取&#xff0c;主要是判断循环依赖是否创建动态代理 进去wrapIfNeces…

【MOOC 作业】第3章 传输层

不是标答也不是参考答案 仅从个人理解出发去做题 1、(20分) ‍主机甲和主机乙之间已建立一个 TCP 连接&#xff0c;TCP 最大段长度为 1000 字节&#xff0c;若主机甲的当前拥塞窗口为 5000 字节&#xff0c;在主机甲向主机乙连接发送 2 个最大段后&#xff0c;成功收到主机乙发…

Java微服务金融项目智牛股 项目简介与金融知识介绍及技术特点

项目简介 金融交易平台服务于金融衍生产品&#xff0c; 包含外汇、贵金属、期货、股票。 各产品具有不同属性与交易规则&#xff0c; 本项目对标MT4等大型交易平台&#xff0c; 遵循FIX全球最广泛的金融市场通用协议。 实现从证券注册开户、行情订阅与呈现&#xff0c; 股票撮合…

JAVA 日期类Date SimpleDateFormat Calendar

1、Date日期类 类 Date 表示一个特定的瞬间&#xff0c;精确到毫秒 1.1 Date的构造函数 Date() 分配一个 Date 对象&#xff0c;以表示分配它的时间&#xff08;精确到毫秒&#xff09; Date(long date) 分配一个 Date 对象&#xff0c;表示自从标准基准时间起指定时间的毫秒数…

2023最新AI创作系统/ChatGPT商业运营版网站程序源码+支持GPT4+支持ai绘画(MJ)+实时语音识别输入+免费更新版本

2023最新AI创作系统/ChatGPT商业运营版网站程序源码支持ai绘画支持GPT4.0实时语音识别输入文章资讯发布功能用户会员套餐免费更新版本 一、AI创作系统二、系统介绍三、系统程序下载四、安装教程五、主要功能展示六、更新日志 一、AI创作系统 1、提问&#xff1a;程序已经支持G…

论洗碗哥在CSDN摸滚打爬的256个日夜

目录 机缘 收获 成就 憧憬 机缘 创作初心了为了记录一下自己的日常学习过程&#xff0c;方便自己日后去总结&#xff0c;或者遇到类似的问题的时候就可以翻阅自己的文章了。也可以加深自己的印象。其实一开始我是一个不太善于总结的人&#xff0c;机缘之下&#xff0c;听到…

机器学习之PCA算法

目录 PCA算法 PCA目标 PCA原理推导 基于最大可分性推导 基于最近重构误差推导 PCA算法流程 PCA优点 PCA缺点 基于PCA的人脸识别 PCA算法 PCA&#xff0c;即主成分分析&#xff08;Principal Component Analysis&#xff09;&#xff0c;是一种常用的降维技术&#x…

【博客674】警惕Prometheus 中的重复样本和无序时间戳错误

警惕Prometheus 中的重复样本和无序时间戳错误 1、场景 您的 Prometheus 服务器日志中是否遇到过以下错误&#xff1f; "Error on ingesting out-of-order samples" "Error on ingesting samples with different value but same timestamp" "dupli…

2023最全 Java 高频面试合集,掌握这些你也能进大厂

进大厂是大部分程序员的梦想&#xff0c;而进大厂的门槛也是比较高的&#xff0c;所以这里整理了一份阿里、美团、滴滴、头条等大厂面试大全&#xff0c;对于 Java 后端的朋友来说应该是最全面最完整的面试备战仓库&#xff0c;为了更好地整理每个模块&#xff0c;我也参考了很…

内网隧道代理技术(六)之 PowerCat反弹Shell

PowerCat反弹Shell PowerCat介绍 PowerCat是一个powershell写的tcp/ip瑞士军刀&#xff0c;看一看成ncat的powershell的实现&#xff0c;然后里面也加入了众多好用的功能&#xff0c;如文件上传&#xff0c;smb协议支持&#xff0c;中继模式&#xff0c;生成payload&#xff…

几分钟带你快速了解SpringBoot框架理论知识!

1.什么是SpringBoot SpringBoot其实就是Spring的子项目。它简化了Spring的开发难度&#xff0c;舍弃了一切可以舍弃的xml配置文件&#xff0c;提供了各种启动器&#xff0c;让程序员上手更快&#xff0c;节省了开发时间。 2.SpringBoot的优点 SpringBoot其实就是对Spring的缺…

抖音林客系统定制开发

抖音林客是一款提供旅游攻略和景点推荐的短视频社交平台&#xff0c;主要用户群体为喜欢旅游和分享生活的年轻人。从需求分析角度来看&#xff0c;可以从以下几个方面进行分析&#xff1a; 信息获取需求&#xff1a;抖音林客用户需求获取有关旅游的详细和实用的信息&#x…