C语言到C++的跨越(2)

本文详细介绍了C++中的函数重载概念,包括其规则和原理,以及引用的特性和与指针的区别。通过实例展示了函数重载如何提高效率,尤其是在参数传递时。
摘要由CSDN通过智能技术生成

函数重载

函数重载概念

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

函数重载的规则

#include<iostream>
using namespace std;
 //参数类型不同

int Add(int a, int b)
{
	cout << "int Add(int* a, int* b)" << endl;
	return a+b;
}

int Add(double a, double b)
{
	cout << "int Add(double* a, double* b)" << endl;
	return a+b;
}
//参数个数不同

void fun()
{
	cout << "void fun()" << endl;
}

void fun(int a)
{
	cout << "void fun(int a)" << endl;
}
//参数类型顺序不同

int div(int a, double b)
{
	cout << "int div(int a, double b)" << endl;
}

int div(double a,int b)
{
	cout << "int div(double a,int b)" << endl;
}

会观察的老铁们已经看出来了,满足函数重载的条件大多是(目前不知道是否还有别的可能)对函数参数进行改动,其余都保持不变(这里有几个易错点,改变函数返回值,改变参数名字这些都不能构成重载,而是重复定义)。

C+函数重载的原理(名字修饰)

在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理编译汇编链接

预处理—>.h文件,.cpp文件:展开头文件,宏替换,条件编译……

编译—>.i文件:检查语法,生成汇编代码。

汇编—>.s文件:把汇编代码编程计算机能看懂的二进制机器码。

链接—>.o文件:生成可执行程序,.exe,.out……

比如当前a.cpp中调用了b.cpp中定义的Add函数时,编译后链接前,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o。链接器看到a.o调用Add,但是没有Add的地址,就会拿着Add这个名字到b.o的中找Add的地址,然后链接到一起。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这里在VS下面看看汇编。在StackInit函数旁边有一个函数的地址。call的本质是跳到函数的地址,也就是函数的一堆指令。在通过jmp跳到函数的真正地址,就是函数的第一句地址(建立函数栈帧)。

Test.cpp只有函数的声明,有函数的定义,才能拿到函数的地址,生成函数汇编指令,Stack.cpp到Stack.o,才有地址。当Test.o到Stcak.o链接的时候,才用地址。链接时,拿着StackInit这个函数名字,去Stack.o去找地址。
什么是链接成功了呢?好比有人承诺给你50w,这个是声明;当你收到了这笔钱,这就是链接成功。同时,Test.cpp中的函数声明也是去检查能不能匹配起来。
总的来说,C语言编译器后,函数的名字修饰不发生变化,而用C++编译器后,函数名字修饰发生改变,不是用简单的函数名字去查找。这就是为什么C++可以寻找到函数地址从而使用重载的函数。

引用

引用概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空
间,它和它引用的变量共用同一块内存空间
引用好比是你的小名,不管叫你的大名还是小名,都是你这个人。
同时这里要注意,引用类型和实体类型必须是同一种

引用特性

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体

常引用

void A()
{
	const int a = 10;
	//int& aa = a;   编译出错,a是只读,不可以扩大权限。

	const int b = 10;
	const int& bb = b;    //权限的平移可以
	int p = b;      //这不是权限放大,这是赋值,p的修改不影响a

	int z = 0;
	const int& zz = z;    //权限的缩小可以
	int& zzz = z;
	zzz++;           //zz不能修改,但是用zzz修改,zz也会变
	const int* p1 = &a;
	p1++;
	//int* p2 = p1;       const修饰的是*p1,p1还是可以改变,但是p2 = p1  p2的值就和p1一样,p1就可以改变了,扩大权限
	double x=3.14;
	const int& y=x;
}

最后一个很特殊,类型转换
当类型不一样的时候,拷贝的时候会先生成一个临时变量。临时变量具有常性,所以要在前面用const修饰。
变量相加的时候也会有这种情况。

引用和指针的区别

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。语法上引用不开空间。但是汇编层面,两者都要开空间。
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  4. 没有NULL引用,但有NULL指针(相对来说)
  5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  6. 有多级指针,但是没有多级引用
  7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  8. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  9. 有些只能用指针做到。比如链表。链表要一个个链接下一个,而这里引用就没办法做到。
#include<iostream>
#include <time.h>
using namespace std;
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{
	A a;
	// 以值作为函数参数
	size_t begin1 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc1(a);
	size_t end1 = clock();
	// 以引用作为函数参数
	size_t begin2 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc2(a);
	size_t end2 = clock();
	// 分别计算两个函数运行结束后的时间
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}
int main()
{
	TestRefAndValue();
}

在这里插入图片描述
这里我们可以看见,由于引用不需要额外占用空间,所以效率比传值快。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值