C++内联/构造函数详解

news2024/11/15 9:51:08

内联函数

宏:

宏的优缺点?
优点:
1.增强代码的复用性。
2.提高性能。
缺点:

1.展开后会使得代码长度变长,使可执行程序变大
2.不方便调试宏。(因为预编译阶段进行了替换)
3.导致代码可读性差,可维护性差,容易误用。(副作用)
4.没有类型安全的检查

先来回顾一下C语言中宏的知识点。

当一段代码被多次反复调用时,如果使用函数封装,将会重复地开辟、销毁多次函数栈帧,从而带来效率上较大的损耗。

为了提高效率,C语言中设计出了宏。宏是一种替换,会将这一段代码完全替换到要使用的地方,不用进行函数调用,从而提高了效率。

然而,宏由于替换的特点,需要格外注意“上下文”,是否需要加“括号”(操作符优先级问题),以及较差的可读性和不可调试的特点,只能适用于一些简单的场景。

总结:宏是用空间换时间,为了提高效率而产生的,但是在使用中又出现了一些其它的缺点。在C++中,产生了一种内联函数,可以专注于用空间换时间的效率提升

Inline函数

升级点:

将短小且会多次调用的一段代码,仍然封装为一个函数,但是要在函数返回值前加上Inline。

在编译时,会将函数体展开(产生和宏一样的替换效果),从而提高效率。

弥补了宏的2个缺点

1、因为变成了一种函数,增加了类型检查,更加安全。

2、变成了函数形式,提高了可读性、可维护性,不容易出现歧义等“副作用”。

调试:

在debug下不可以调试,但可以修改编译器属性,使其可以调试。

在release版本下可以进行调试。

这样又解决了宏不可调试的缺点。

编译器优化:

inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建
议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、
是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。

简单来说:编译器会进行一个优化操作。

对于时间换空间,我们要在合理的范围内进行,比如牺牲5倍空间,换来2倍效率的提升是值得的,但是如果牺牲10000倍空间,就有些不值得了,不同编译器对于此尺度是不同的。如果内联函数过长,就会被直接忽略,不再展开,而是仍然进行函数调用call指令

举一个例子:如果inline函数仅有3行代码,并且被调用了1W次。转换成指令就变成了3W行。

若不使用inline函数,指令有(1W+3)行(1W次call,加上函数本身)这样的尺度是可以接受的。

如果inline函数有100行代码,指令为100W行。

不使用inline函数,指令仅有(1W+100)行,这样的话会使代码变得过长,编译器就会忽略了。

声明定义不分离:

由于inline函数的初衷是完成替换功能,而不是call指令,因此内联函数在翻译过程中是不会进行符号表,即不会参与后续的链接过程。

因此,当内联函数的定义与声明分离时,编译器就找不到要替换的部分了,就会产生错误。

必须要在哪个源文件使用,就在那里定义。

或者定义在头文件中,然后哪个源文件使用,就去包含头文件,即把函数的定义拷贝过去。

封装:

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来
和对象进行交互。
我们可以定义一个类,并将这个类实例化为几个对象。

可以利用类中定义public的函数/方法对这些对象进行相关操作。

封装后,我们便不能在类的外面对于非公有变量进行操作。在C++中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。

this指针:

编译器实现原理:

我们先定义了一个Date的类,在C++中,成员函数都是定义在公共区域的,每次示例化的过程,开辟空间的只有成员变量(因为函数实现的功能是相同的,为了避免空间浪费)。

 d1和d2调用的都是同一个Print函数,编译器底层是如何区分出d1和d2的呢?

 在汇编中我们可以看到,编译器实际上将d1和d2的地址分别传给了Print函数,从而区分两个对象并且方便后续对不同对象内部进行相应操作。

但是在C++代码中,我们并没有传入&d1和&d2,编译器是如何实现的呢?

C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏
的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”
的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编
译器自动完成。

简单来说,编译器内部使得每一个成员函数都多了一个 DataType * const this 类型的指针,可以接收不同对象的不同地址,const的修饰使得this只能指向外部指定的这个对象。

这个this指针是隐式传递的,不可以显式传递和接收。

 在成员函数内部可以使用this指针。

this指针特性

1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
2. 只能在“成员函数”的内部使用
3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给
this形参所以对象中不存储this指针
4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传
递,不需要用户传递
因为this指针是形参,所以它存在栈区。

this指针能否为空?

 首先我们要知道,对空指针解引用是一种运行错误,而不是编译错误。

先是定义了一个A类的指针p,且p为空指针。

C++代码上,p->Print()看似是对p进行解引用,但因为成员函数都放在公共区域,并不需要对象的指针p去寻找,即底层上并没有对p进行解引用,只是将p(对象的地址)传入Print函数中,并用this指针这个形参进行接收。

然后要看在成员函数内部是否对this空指针进行解引用。

情况1中没有,因此可以正常运行。情况2中解引用了,产生执行错误。

因此,this指针可以接收为空,只要不解引用即可。

构造函数:

在C语言中,我们有时可能会忘记进行初始化和销毁,在C++中,构造函数和析构函数可以进行编译器自动初始化和销毁。

在空类中编译器会生成6个默认成员函数。

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

构造函数是一个特殊的成员函数,名字与类名相同,实例化对象时由编译器自动调用,以保证
每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象

特性:

1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器自动调用对应的构造函数。
4. 构造函数可以重载
5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦
用户显式定义编译器将不再生成

6.对内置类型成员不初始化,自定义成员初始化。
注:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在
类中声明时可以给默认值。

7. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个
注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为
是默认构造函数。

typedef int DataType;
class Stack
{
public:
	//  3、对象示例化时自动调用构造函数  1、同类名  2、无返回值  4、可以重载
	Stack(DataType capacity = 3)
	{
		cout << "Stack(DataType capacity = 3)" << endl;
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	//  构造函数支持重载的另一种初始化方式
	Stack(DataType* a, int n)
	{
		_array = (DataType*)malloc(sizeof(DataType) * n);
		if (nullptr == _array)
		{
			perror("malloc fail");
			return;
		}

		memmove(_array, a, sizeof(DataType) * n);
		_capacity = n;
		_size = n;
	}

	void Push(DataType data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};

这里我用顺序栈来演示。

写一个与类同名的Stack函数,且不用写返回值。 完成对容量、个数和数组的初始化。1-2

在对象实例化后自动调用,完成了初始化。 3

支持函数重载(用参数来区分同名函数)。

这里我用一个外部的数组a,共有n个元素,对这个顺序栈进行初始化。4

 

 将两个构造函数Stack都屏蔽后,编译器生成了一个无参的默认构造函数。这个默认构造函数对自定义类型进行初始化,对于内置类型不初始化,即这里的_size和_capacity都变为了随机值。5

 

 声明的时候可以给一个缺省值,这样一来,当我们不自己定义构造函数时,编译器自己产生的也可以帮助我们把内置类型给完成初始化6

  1、一般情况下,如果有内置类型成员,需要自己写构造函数
  2、当全部都是自定义成员时,可以让编译器自己生成构造函数

无参的构造函数和全缺省的构造函数都称为默认构造函数,它们存在时,编译器就不会自动生成了,之前C++11的补丁也就与这里无关了。


 无参数时产生歧义,编译器不知道要调用哪个构造函数。

传入任意一个参数就不会报错。

当然,如果其中一个不是全缺省,即需要至少一个参数,无参数时就能区分,也不会报错了。7

目录

一、内联函数

宏:

Inline函数

升级点:

调试:

编译器优化:

声明定义不分离:

封装:

this指针:

编译器实现原理:

this指针特性

this指针能否为空?

构造函数:

特性:


 

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

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

相关文章

Python 查看数据常用函数

Python 查看数据常用函数&#xff08;以 iris 数据集为例&#xff09; 1、查看前后几行数据&#xff1a;head 和 tail2、查看数据基本信息&#xff1a;info3、查看数据统计信息&#xff1a;describe 查看数据可以用很多函数&#xff0c;这里就挑选几个最常用的进行简单展示&…

SpringBoot自动配置原理、手写一个xxx-spring-boot-starter

SpringBoot的自动配置是&#xff1a;当项目中使用了一个第三方依赖&#xff0c;如何将第三方依赖中的Bean加载到Spring的IOC容器中&#xff0c;我们就可以做到无需额外的配置&#xff0c;直接使用第三方jar中的Bean。 SpringBoot的理念是“约定大于配置”&#xff0c;只要按照S…

【下载器篇】IDM下载记录分析(简)

【下载器篇】IDM下载记录分析&#xff08;简&#xff09; IDM下载记录分析-未完待续—【蘇小沐】 文章目录 【下载器篇】IDM下载记录分析&#xff08;简&#xff09;1.实验环境 &#xff08;一&#xff09;IDM下载记录分析-未完待续临时文件夹下载痕迹 总结 1.实验环境 系统版…

【内网渗透】春秋云镜Intitle WP

前言 第一次正式接触内网渗透的东西&#xff0c;写的很新手&#xff0c;也适合新手观看&#xff0c;有问题可以私信或评论&#xff0c;接下来会持续更新 信息收集 拿到地址先nmap扫端口 没什么发现&#xff0c;直接访问80端口&#xff0c;看到图标知道是thinkphp 第一台Th…

leetcode刷题(8)二叉树(2)

各位朋友们&#xff0c;大家好&#xff01;今天我为大家分享的是关于二叉树leetcode刷题的第二篇&#xff0c;我们一起来看看吧。 文章目录 1.对称二叉树题目要求示例做题思路代码实现 2.二叉树的最大深度题目要求示例做题思路代码实现 3.翻转二叉树题目要求示例做题思路代码实…

WebSocket入门

WebSocket 1.1websoket介绍 websocket是一种网络通信协议&#xff0c;RFC6455定义了它的通信标准 websocket是Html5开始提供的一种在单个TCP连接上进行全双工通讯的协议 Http协议是一种无状态、无连接、单向的应用层协议&#xff0c;它采用了请求/响应模型&#xff0c;通信…

Tomcat多实例部署实验

引言 本文主要内容是tomcat的多实例配置实验。 一、实验准备 Tomcat多实例是指在一台设备上运行多个Tomcat服务&#xff0c;这些Tomcat相互独立&#xff0c;互不影响。多实例与虚拟主机不同&#xff0c;虚拟主机的本质是在一个服务下有多个相对独立的目录&#xff0c;但是多实…

OFA(One-For-All)阿里达摩院实现架构、模态、任务的三个统一之Image Captioning

OFA(One-For-All) 通用多模态预训练模型&#xff0c;使用简单的序列到序列的学习框架统一模态&#xff08;跨模态、视觉、语言等模态&#xff09;和任务&#xff08;如图片生成、视觉定位、图片描述、图片分类、文本生成等&#xff09; 架构统一&#xff1a;使用统一的transfo…

何谓SRIO——RapidIO之旅从这里开始

何谓SRIO——RapidIO之旅从这里开始 SRIO&#xff08;Serial RapidIO&#xff09;协议是一种用于高速串行通信的协议&#xff0c;旨在连接数字信号处理器&#xff08;DSP&#xff09;、网络处理器、FPGA等芯片&#xff0c;以及它们之间的互连。SRIO协议具有低延迟、高带宽&…

【单链表】

单链表 1. 函数的声明部分2. 函数的实现部分&#xff08;1&#xff09;打印链表&#xff08;2&#xff09;头插&#xff08;3&#xff09;尾插&#xff08;3&#xff09;头删&#xff08;4&#xff09;尾删&#xff08;5&#xff09;单链表的查找&#xff08;6&#xff09;删除…

leetcode 879. Profitable Schemes(有利润的计划)

有几个工程&#xff0c;每个工程需要group[ i ]个人去做&#xff0c;做完了可以得到profit[ i ]的利润。 现有2个限制条件&#xff1a; 人数上限是n, 且参加了一个工程的人不能再参加其他工程。 利润下限minProfit, 至少要获得minProfit的利润。 问有多少种工程的选法&#xff…

Zuul源码解析(一)

说在前面 我们公司有一个线上服务报错通知群&#xff0c;经常报网关服务的一个 EOFException 异常。这个异常报出来好久了&#xff0c;如下图所示&#xff0c;艾特相关的人也不去处理&#xff0c;大概是不重要异常吧&#xff0c;反正看样子是不影响线上核心业务流程。 然后我上…

FreeRTOS学习笔记(一)——初识FreeRTOS

FreeRTOS官网&#xff1a;FreeRTOS - 适用于具有物联网扩展功能的嵌入式系统的市场领先 RTOS&#xff08;实时操作系统&#xff09; FreeRTOS源码下载&#xff1a;FreeRTOS Real Time Kernel (RTOS) - Browse /FreeRTOS at SourceForge.net 目录 0x01 FreeRTOS编程风格 一…

用CentOS服务器自己搭建部署个Discuz论坛网站,网站搭建教程

Linux系统CentOS服务器使用堡塔搭建论坛网站全套教程。服务器大本营&#xff0c;技术文章内容集合站发车啦&#xff01; 操作系统&#xff1a;Centos 7.6 网站程序&#xff1a;Discuz-X3.4 前言 首先&#xff0c;搭建一个网站需要准备&#xff1a;服务器、域名、网站程序。 …

PWM控制直流电机

一&#xff0c;TB6612电机驱动模块 直流电机属于大功率器件&#xff0c;GPIO无法直接驱动&#xff0c;需要电机驱动模块配合&#xff0c;才能驱动直流电机. TB6612可以驱动2个直流电机。由IN1&#xff0c;IN2控制电机旋转方向&#xff0c;由PWM控制电机旋转速度。 二&#xf…

基于Oracle VM VirtualBox的ubuntu的安装

基于Oracle VM VirtualBox的ubuntu的安装 感谢詹老师的帮助使我得以完成本次安装&#xff0c;以下为本次安装的一个小小的记录。 目录 基于Oracle VM VirtualBox的ubuntu的安装Oracle VM VirtualBox的下载与安装ubuntu的下载Oracle VM VirtualBox下安装ubuntu安装 ROS Melodi…

GitHub 开源神器 Bark模型,让文本转语音更简单

今天跟大家分享一个文本转语音的开源模型&#xff1a;Bark Bark 是由Suno创建的基于转换器的文本到音频模型。Bark 可以生成高度逼真的多语言语音以及其他音频 - 包括音乐、背景噪音和简单的音效。该模型还可以产生非语言交流&#xff0c;如大笑、叹息和哭泣。 该项目刚开源不…

二叉树OJ题(C++实现)

文章目录 1.二叉树创建字符串2. 二叉树的最近公共祖先3.二叉搜索树与双向链表4.从前序与中序遍历序列构造二叉树 1.二叉树创建字符串 二叉树的层序遍历 OJ连接 主要思路是借助一个队列&#xff0c;将每一层的数据以size统计&#xff0c;当size为0时说明该层数据已经输入完&…

Unity Physics2D 2d物理引擎游戏 笔记

2d 材质 里面可以设置 摩擦力 和 弹力 Simulated&#xff1a;是否在当前的物理环境中模拟&#xff0c;取消勾选该框类似于Disable Rigidbody&#xff0c;但使用这个参数更加高效&#xff0c;因为Disable会销毁内部产生的GameObject&#xff0c;而取消勾选Simulated只是禁用。…

详解C语言string.h中常用的14个库函数(四)

本篇博客会讲解最后4个函数&#xff0c;分别是memset, memcpy, memmove, memcmp。这4个函数开头都是mem&#xff0c;即memory&#xff08;内存&#xff09;的缩写。 memset void * memset ( void * ptr, int value, size_t num );memset可以用来设置内存中的值。该函数可以把从…