欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-03-27 13:06:14
...

函数

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

函数在类中被称作方法(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){}		//①
void func(const int& i) {}	//②
//可以区分
int a = 1;
func(a);//传入变量,调用①
func(1);//传入常量,调用②

指针

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

内联函数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
相关标签: c++