《Cpp-Primer-Plus》-第八章 函数探幽

91 阅读6分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

文章目录

第八章 函数探幽

博文地址
本文记录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来分配新的内存空间。

​ 那么,何时使用引用参数呢?

  • 对于使用传递的值而不作修改的函数:

    1. 如果是内置数据类型或小型结构,则按值传递
    2. 如果是数组,而必须使用指针,而且是const指针
    3. 如果是较大的结构,则使用const指针或const引用
    4. 如果是类对象,则使用const引用
  • 对于需要修改参数的函数:

    1. 如果是内置数据类型或小型结构,则使用指针
    2. 如果是数组,则使用指针
    3. 如果是结构,则使用引用或指针
    4. 如果是类对象,则使用引用

默认参数

​ 当函数调用中省略了实参时自动使用的值,就是默认参数。

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 &);

​ 同时,若选择上述原型时,非模版版本优先于显式具体化和模版版本,显式具体化优先于使用模版生成的版本。

复习题

  1. 哪种函数适合定义为内联函数

    只有一行代码的小型,非递归函数