本文已参与「新人创作礼」活动,一起开启掘金创作之路。
文章目录
第八章 函数探幽
博文地址
本文记录CCPlus第八章知识
知识整理
c++在函数方面,相比于c,还有许多新特性,如内联函数,按引用传递变量,默认的参量值,函数重载和模版函数
内联函数
要知道内联函数与普通函数的区别,就要弄清楚两者调用的区别。
普通函数调用使程序跳到另一个地址,即函数开头地址执行,并在函数结束时返回,来回跳跃并记录跳跃位置需要一定的开销,而内联函数不需要跳到另一个位置处执行代码,它是使编译器使用相应的函数代码替换函数调用,所以它的运行速度比普通函数快,但代价是需要占用更多的内存。
所以,不能滥用内联函数。如果执行代码的时间比处理函数调用的时间长,则只节省很少的时间,得不偿失,如果函数代码执行时间很短,则可以使用内联函数,可节省大量时间。
内联函数关键字为inline
,并且不需要原型,把整个函数头与函数体放在原型位置就行。
inline double squre(double x) {return x*x};
引用变量
引用是已定义变量的别名,引用变量的主要用途是用作函数的形参,这时函数将使用原始数据,而不是其副本。
int rats;
int & todents = rates;
c++用&
符号来声明引用,这时两者的地址和值是一样的,改变其中一个,两个都会改变。同时需要注意的是在声明引用时必须初始化,而不能先声明,在赋值,初始化后就与这个变量永远关联起来,改一个,则两个都改。
引用通过用作函数参数,函数中使用的将不是实参的副本,而是实参本体,改变形参,实参也会跟着改变,类似指针传递,在某种程度上可以增加效率,在引用通常不用在内置类型中,而用于结构和类参数。
struct free{
int made;
int attempts;
int percent;
};
void display(const free & ft);
void set_pc(free & ft);
free & accumulate(free & target, const free & source);
上面有三种引用传递结构做为参数的函数,
display
函数由于有const
,所以只能使用ft
,而不能修改,使用引用传递比起使用按值传递可节省时间和内存。
set_pc
函数修改ft
的值会影响原数据
accumulate
函数比较特别的是,它返回的是free引用,如果返回没有引用,它将返回其副本,但加上引用的话,它则直接返回其free,即target。如dup = accumulate(team, five);
语句,如果返回的是一个结构,而不是指向结构的引用,则会把整个结果复制到一个临时位置,再将这个拷贝复制给dup,但在返回值为引用时,将直接把team赋值到dup,其效率更高。
返回引用需要注意,要避免返回一个函数终止时不再存在的内存单元引用,也不能返回指向临时变量的指针。要避免这种问题,有两种方法,第一种是返回一个作为参数传递给函数的引用,另一个是用new来分配新的内存空间。
那么,何时使用引用参数呢?
-
对于使用传递的值而不作修改的函数:
- 如果是内置数据类型或小型结构,则按值传递
- 如果是数组,而必须使用指针,而且是const指针
- 如果是较大的结构,则使用const指针或const引用
- 如果是类对象,则使用const引用
-
对于需要修改参数的函数:
- 如果是内置数据类型或小型结构,则使用指针
- 如果是数组,则使用指针
- 如果是结构,则使用引用或指针
- 如果是类对象,则使用引用
默认参数
当函数调用中省略了实参时自动使用的值,就是默认参数。
char * left(const char * str, int n = 1)
n=1
即为默认值,若函数调用时不指定n为多少,则n默认为1。
带参数列表的函数,必须从右向左添加默认值,也就是说,要给某个参数设置默认值,它的右边的所有参数必须提供了默认值。
函数重载
函数重载可以使用多个同名的函数。
但如何区分不同函数?
关键是函数的参数列表,也叫做函数特征标。C++允许定义名称相同的函数,条件是它们的特征标不同。如果参数数目或参数类型不同,则特征标不同。
void print(const char * str, int width);
void print(double d, int width);
void print(long l, int width);
void print(int i, int width);
void print(const char *str);
编译器检查函数特征标时,不区分类型引用和类型本身,也不区分const
和非const
变量。
那么,何时使用函数重载?仅当函数基本上执行相同的任务,但使用不同形式的数据时,才使用函数重载。
函数模版
函数模版使用泛型来定义函数,通过将类型作为参数传递给模版,可使编译器生成该类型的函数。
template <typename, T>;
void swap(T &a, T &b){
T temp;
temp = a;
a = b;
b = temp;;
}
模版也可以重载,就像重载常规函数定义那样重载模版定义
template <typename, T>
void swap(T &a, T &b);
template <typename, T>
void swap(T *a, T *b, int n);
编写的模版可能无法处理某些类型,这时可以为特定类型提供具体化的模版定义,称为显式具体化
struct job{
char name[40];
double salary;
int floor;
};
template <> void swap<job>(job &, job &);
同时,若选择上述原型时,非模版版本优先于显式具体化和模版版本,显式具体化优先于使用模版生成的版本。
复习题
-
哪种函数适合定义为内联函数
只有一行代码的小型,非递归函数