【C++学习记录】函数、链接器与函数的声明和定义、函数默认参数、函数重载、内联函数

函数

定义:编写的代码块,被设计用来执行特定的任务。

函数在类中被称作方法(methods)

函数的形参在定义时就已经分配了内存,只有在函数被调用时才会初始化。

主要目的:防止大量代码重复。在需要复制粘贴大量代码的地方改为使用函数可提高效率、减少错误。

太多的函数调用会减慢运行速度(非内联(inline)函数时):每次调用函数,编译器产生一个调用指令。
为了调用一个函数,需要为该函数创建一个栈框架(stack frame),将参数、返回地址等推(push)到栈上;然后跳到程序的相应位置,执行那个函数;函数执行完后返回保存在栈上的数据,回到调用函数的地方。
因此反复地调用函数会减慢程序的运行速度。

在使用if-else分支语句时,执行的代码会随着分支条件的判断而跳转,如果“语句和分支条件分布在内存距离较远的地方”,代码会比较低效,所以很多优化过的代码会避免分支结构。

不论是传值方式或是传地址(指针或引用)方式,都可以看做形参在栈内存中开辟了新的空间并复制了实参的值。因为传地址方式传递的是地址(而地址通常不是用户所关心的数据),所以函数体内可以通过对地址的解引用操作修改实参的值。
函数可以重复声明,不可重复定义。所以通常在头文件(.h)中存放声明,在cpp文件中存放定义。

链接器(linker)的工作方式
在main()中调用一个函数func()时:
如果main.cpp文件(存放main函数的文件)中没有func()函数的声明和定义,会直接报错“未定义标识符(编译错误,C开头)”。在该文件中添加func()函数的声明后,文件单独编译成功(ctrl+F7)。
如果在该项目的其他cpp文件中没有func()函数的定义,则在生成项目(ctrl+b)时报错“无法解析的外部命令(链接错误,LNK开头)”。

为什么声明了一个函数即使没有定义,也能编译成功?
声明func()函数相当于告诉编译器:“这个项目中的某一个文件中存在一个名为func()的函数定义,而我将要用到它。”编译器完全“相信了”我们的鬼话,所以单独编译(compile)一个文件时不会报错。
而当生成(build)整个项目时,链接器会根据main()函数中调用的func()函数的声明在整个项目中寻找func()函数的定义,然后将主文件中的调用链接起来;如果找不到func()函数的定义,就会发生链接错误(linking error)

函数的默认参数
可以在声明中或定义中设置默认参数,二者选一,不能同时在二者中设置默认参数。
默认参数从右往左定义,调用函数时传入的参数则从左往右赋值。
注意事项:
1、在声明中使用默认参数时可省略形参名

void func(int = 111, int = 222);
void func(int x, int y)
{
	cout << "first: " << x << " second: " << y << endl;
}

2、可以使用全局变量作为默认参数

int x = 111;
void func(int = x, int = 222);
void test()
{
	int x = 333;
	func();
}

test函数输出的值为111和222,func函数的默认值在声明中已经设置好了,不会受test的成员变量x影响

3、当调用一个使用了默认参数的函数时,默认参数的必须在调用该函数前设置好(上例),否则编译器无法知道默认参数的存在

void func(int,int);
void test()
{
	int x = 333;
	func();
}

void func(int x = 111, int y = 222)
{
	cout << "first: " << x << " second: " << y << endl;
}

编译失败。“必须为func函数传入2个int类型数据”

函数重载
同名函数,编译器通过参数类型或参数个数分辨不同的重载版本。
使用默认参数的重载函数会“减少参数个数”,可能引发编译错误。
对于const参数:
普通变量

void func(int i){}
void func(const int i) {}
//无法区分函数

引用

void func(int& i){}		//A
void func(const int& i) {}	//B
//可以区分
int a = 1;
func(a);//传入变量,调用A
func(1);//传入常量,调用B

指针

void func(int* i){}		//A
void func(const int* i){}	//B
void func(int* const i){}	//C	
//①与③会引发冲突:“函数“void func(int *)”已有主体”
//将函数定义③删除后:
int a = 1;
int* p1 = &a;
const int* p2 = &a;
int* const p3 = &a;
func(p1); //调用A
func(p2); //调用B
func(p3); //调用A

内联函数inline
目前能理解的部分:定义为内联函数的函数在被调用时,整段函数代码被粘贴到函数被调用的位置。
使用内联函数能避免一般函数调用时的压站退栈开销,很适合经常使用的的小型函数。
编译器有时会忽略内联修饰符,将其修饰的函数作为普通函数处理。

函数的返回值为引用类型(暂时还不知道用处,先记着)

int& func()
{
int a = 10;
return a;
}

int& a = func();
int b = func();
cout << a << "   " << b << endl;
cout << a << "   " << b << endl;
cout << a << "   " << b << endl;
输出结果:
10   10
2027202960   10
2027202960   10

变量a的数据在被保留一段时间后被释放。

函数的调用也可以作为左值

int& func()
{
static int a = 10;
return a;
}

cout << func() << endl;
func() = 20;
cout << func() << endl;
//输出	10		
		20
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值