【C++】函数重载、引用、内联函数。

目录

一、函数重载的概念

具体实现例子

同名函数的参数个数不同

同名函数的参数类型不同

同名函数的参数类型顺序不同

C++支持函数重载的原因

补充:

二、引用

引用概念:

具体用法:

实际应用场景:

场景一:做参数

 场景二:做返回值

常引用

补充:

三、内联函数


一、函数重载的概念

函数重载:是函数的一种特殊情况,C++允许在同一作用域声明几个功能类似同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

注意:C语言是不支持函数重载的。

具体实现例子

同名函数的参数个数不同

#include<iostream>

using namespace std;


int Add(int x, int y) {

	return x + y;
}

int Add(int x, int y, int z) {

	return x + y + z;
}


int main() {

	//同名函数的参数个数不同
	int x = Add(1, 2);
	int y = Add(1, 2, 3);

	cout << "x = " << x << endl;
	cout << "y = " << y << endl;

	return 0;
}

输出结果:


同名函数的参数类型不同

#include<iostream>

using namespace std;


int Add(int x, int y) {

	return x + y;
}

double Add(double x, double y) {

	return x + y ;
}


int main() {

	//同名函数的参数类型不同
	int x = Add(1, 2);
	double y = Add(1.2,1.2);

	cout << "x = " << x << endl;
	cout << "y = " << y << endl;

	return 0;
}

输出结果:


同名函数的参数类型顺序不同

#include<iostream>

using namespace std;


int Add(int x, double y) {

	return x + y;
}

double Add(double x, int y) {

	return x + y ;
}


int main() {

	//同名函数的参数类型不同
	int x = Add(12.2, 2);
	double y = Add(1.2,1);

	cout << "x = " << x << endl;
	cout << "y = " << y << endl;

	return 0;
}

输出结果:


C++支持函数重载的原因

主要是由于在C++语法中,编译器会对函数名在编译的进行修饰。

而C语言是不会对其函数名进行修饰的,所以C不支持重载。

如下:

//gcc编译器下,编译器对函数名的修饰规则大致为:
//_Z3Adddi 
//其中,_Z是固定前缀,Add是函数名,后面两个d i 指的是函数参数类型的首字母
double Add(double x, int y) {

	return x + y ;
}

如此,便可以简单理解下为什么C++支持重载,并且了解为什么要同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同了。这样函数名经过编译器修饰后,便可以看成不同的函数来使用了。

补充:

如果两个函数的函数名和参数是一样的,返回值不同是不构成重载的。


二、引用

引用概念:

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

例如,一人之下国漫中,王也这个人物,有个外号叫也总,亲近点的人小王也

也总是指王也这个人,王也这个人可以是也总,小王也也是王也也可以是也总。

总之都是指的同一个人。

所以,简单点来说,其实引用在现实生活中就相当于给变量取了个外号了。


特性:

1.引用在定义时必须要初始化

2.一个变量可以有多个引用

3.引用一旦引用一个实体,再不能引用其他实体


具体用法:

类型 &   引用变量名(对象名) = 引用实体;

int x = 0;

int& y = x;

这样,y也就是可以看作是x了,共用同一块内存空间。


实际应用场景:


场景一:做参数

主要是输出型的参数,形参的改变,影响实参。

场景的导入:

如果我们想使用一个函数来交换两个数的位置,代码你会怎么写呢?

是这样么?

void Swap(int x, int y) {

	int tmp = x;
	x = y;
	y = tmp;
}

int main() {

	int x = 3;
	int y = 5;

	Swap(x, y);//交换两个数的位置的函数

	cout <<"x = " << x << endl;
	cout <<"y = " << y << endl;

	return 0;
}

运行结果如下:

以上我们发现其函数根本没有发挥作用,其数据根本没有进行交换。

那么这是为什么呢?

是因为在函数的调用中,形参只是实参的一份临时拷贝,对形参的改变或进行相关的操作不会影响到实参。


那么在C语言中会如何做呢?

如下:

C语言采用了指针的方法来解决,通过对指针进行解引用,来实现交换函数功能。


那么C++是如何解决的呢?

使用引用来解决这个问题就比较方便了,比使用指针方便一点

在调用参数的时候,对其参数进行引用就可以了,这样就可以把形参和实参看成同一个变量,于是在Swap函数调用时,对形参的改变,其同时也对实参进行了改变了。

注意引用类型引用实体必须是同种类型的。


 场景二:做返回值

1.传值返回(函数返回时,返回对象已经还给系统时使用)

这里n变量已经还给系统了。


2.传引用返回(函数返回时,返回对象还没还给系统时使用。如:静态,全局,上一层栈帧,malloc出来的等)

这里n变量在静态区,未还给系统。 

易错点一:

以上代码编译不通过。

这是因为 Count() 函数返回的是一个新创建的临时变量,即 n 变量的一个副本,只是值相同。并且该临时变量会被定义成 const 类型,所以无法修改。

解决方法是返回变量 n 的引用。

即写成:

const int & ret = Count();

额外补充:

这里临时变量会被定义为const类型的主要原因是,在进行类型转换时,都会产生临时变量,即其需要转换变量的一个副本,只是他们两的值相等,并且该临时变量会被定义成const类型,所以无法修改,相当于一个常变量。

例如:

int i = 0;

double & di = i;//编译不通过。这里进行了类型转换。

const double & di = i;//编译通过

易错点二:

int& Mul(int a, int b) {
	int c = a * b;
	return c;
}

int main() {
	int& ret = Mul(1, 2);
	Mul(3, 4);

	cout << "Mul(1,2) is:" << ret << endl;
	cout << "Mul(3,4)is:" << ret << endl;
	return 0;
}

输出结果:

Mul函数中 c 没用static 修饰的话,则会随着Mul函数的调用完成后,一同销毁掉。所以不能使用引用返回,得使用传值返回。

这里的销毁指的是对这块空间没有使用权了,系统回收后可能会将这块空间存储其他数据,也可能继续暂时存储n的数据。

所以如果这时继续使用传引用返回,则会造成返回结果未知。

传引用返回的实际应用:

#define N 10//定义数组的长度

typedef struct Array {

	int a[N];

	int size;
}Array;

//引用返回
//1.减少拷贝
//2.调用者可以修改返回对象

int& PosAt(Array& arr, int i) {
	assert(i < N);
	
	return arr.a[i];

}

int main() {

	Array arr;
	for (int i = 0; i < N; i++) {

		PosAt(arr, i) = i * 10;//修改返回对象

		cout << arr.a[i] << endl;
	}

	return 0;
}


常引用

无法对常量进行引用

int main() {


	const int a = 10;
	int& ra = a;//这里会报错

	return 0;
}


补充:

补充一:

typedef struct Node{

	struct Node* next;
	int val;

}Node,*PNode;//给Node这个结构体起了两个别名,Node和一个它的指针名*PNode

void PushBack(PNode& phead, int x) {//这里的 PNode& 指的是对 Node*起的别名,对其的引用
	Node* newNode = (Node*)malloc(sizeof(Node));
	
	if (phead == NULL) {

		phead = newNode;

	}
}

补充二:

指针和引用,在赋值或初始化的时候,权限可以缩小,但是不能放大。

注意:这个前提是在使用指针或者引用的时候才算成立。

int main() {

	//权限放大
	const int a = 2;//只读不能写
	int& b = c;//可读可写,编译不通过
	
	const int* pi = NULL;//只读不能写
	int* p2 = pi;//可读可写,编译不通过

	//权限保持
	const  int d = 1;//只读不能写
	const int& e = d; //只读不能写

	const int* pj = NULL;//只读不能写
	const int* p2 = pj;//只读不能写

	//权限缩小
	int f = 2;//可读可写
	const int g = f;//只读不能写

	int* pk = NULL;//可读可写
	const int* p3 = pk;//只读不能写




	return 0;
}

引用和指针的不同点:
1.引用概念上定义一个变量的别名,指针存储一个变量地址。
2.引用在定义时必须初始化,指针没有要求
3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何-个同类型实体
4.没有NULL引用,但有NULL指针
5.在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
6.引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
7.有多级指针,但是没有多级引用
8.访问实体方式不同,指针需要显式解引用,引用编译器自己处理
9. 引用比指针使用起来相对更安全

 

三、内联函数

定义:

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。

内联说的以时间换空间指的是编译时期的空间,并且内联不一定会调用,只是给编译器的建议。有可能会在编译的被call,也就是跳过。

//C++推荐
//const和enum替代宏常量
//inline去替代宏函数
//
//
//宏缺点:
//1、不能调试
//2、没有类型安全的检查
//3、有些场景下非常复杂,容易出错,不容易掌握
//
//
//要求实现ADD宏函数
#define Add(x,y) ((x) + (y))

inline int Add1(int x, int y) {

	int z = x + y;
	return z;

}

int main() {

	//问题一:Add(x , y)(x) + (y) 外面的大括号不要可以么?不行
	//如下,如果不加外面大括号,则会变成 (1) + (2) * 3.
	//而原本你是想要 1 + 2 的 和 乘以 3 的 。但由于乘的优先级高于加法现在变成了 1 + (2 * 3)
	Add(1, 2) * 3;



	//问题二:Add(x,y) (x + y)里面的大括号不要可以么?不行
	//由于宏是直接暴力替换的
	//所以以下程序在不加里面的大括号的时候
	//调用宏函数的时候会变成(x & y + x | y).
	//而你原本是想要 x&y 的结果与 x|y的结果进行相加了。
	int x = 1, y = 2;
	Add(x & y, x | y);


	//用内联函数来替代
	int ret = Add1(1, 2);
	cout << ret << endl;


	return 0;
}

注意:内联函数的声明和定义应该合在一起,而不是分开。否则会产生链接错误。

假如一旦只在.h文件中对函数进行声明,在.cpp文件中对函数进行定义。那么main函数入口在调用这个函数的时候,只会包含以下.h文件,从而导致在编译阶段没有找到这个对应函数的定义,就会导致报链接错误!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值