《C++ Primer Plus》第8章学习笔记
2021-06-10 本文已影响0人
蓬篙人
内容思维导图
第8章 函数探幽1. 内联函数
- 常规函数和内联函数的主要区别不在于编写方式,而在于C++编译器如何将它们组合到程序中。对于内联代码,程序无需跳到另一个位置处执行代码,然后再跳回来。因此,内联函数的运行速度比常规函数快。
- 使用内联函数必须采取下述措施之一:
- 在函数声明前加上关键字
inline
。
- 在函数声明前加上关键字
- 在函数定义前加上关键字
inline
。
- 在函数定义前加上关键字
// 内联函数定义
inline double square(double x) { return x * x; }
int main()
{
// 函数调用
}
2. 引用变量
- 引用变量的声明
int& refer = 6;
- 必须在声明引用时进行初始化,而不能像指针那样,先声明,再赋值。
// 下面的代码是错误的
int rat;
int& rodent;
rodent = rat;
- 引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名,这种传递参数的方法称为按引用传递。
- 按引用传递与按值传递的外在区别是声明函数的方式:
void swapr(int& a, int& b); // 按引用传递
void swapv(int a, int b); // 按值传递
- 传递引用与传递指针的区别:
- 声明函数参数的方式。
- 指针版本在使用参数值需要使用解除引用操作符
*
。
- 指针版本在使用参数值需要使用解除引用操作符
-
const
引用参数创建临时变量的情形:- 实参的类型正确,但不是左值(非左值:字面常量或多项表达式)。
- 实参的类型不正确,但可以转换为正确的类型。
- 将引用参数声明为常量数据的理由:
- 使用
const
可以避免无意中修改数据的编程错误。
- 使用
- 使用
const
使函数能够处理const
和非const
实参,否则将只能接受非const
数据。
- 使用
- 使用
const
引用使函数能够正确生成并使用临时变量。
- 使用
- 记住:返回引用的函数实际上是被引用的变量的别名。
- 返回引用应注意的问题:应避免返回当函数终止时不再存在的内存单元引用。解决方法:
- 返回一个作为参数传递给函数的引用。
- 用
new
来分配新的存储空间(隐藏new
调用,应注意使用delete
来释放)。
- 用
- 将
const
用于引用的返回类型,只意味着你不能使用返回的引用来直接修改它指向的值。 - 使用引用参数的两个主要原因:
- 程序员能够修改调用函数中的数据对象。
- 提高程序运行速度。
- 函数传递类型选择的指导原则:
- 数据对象很小,则按值传递。
- 数据对象是数组,则使用指针,并将指针声明为指向
const
的指针。 - 数据对象是较大的结构,则使用
const
指针或const
引用。 - 数据对象是类对象,则使用
const
引用。传递类对象参数的标准方式是按引用传递。
3. 默认参数
- 默认参数指的是当函数调用中省略了实参时自动使用的一个值。
char* left(const char* str, int n = 1); // 参数n默认值为1
- 对于带参数列表的函数,必须从右向左添加默认值。
int chico(int n, int m = 6, int j); // 错误!!!
- 只有原型指定了默认值,函数定义与没有默认参数时完全相同。
4. 函数重载
- 术语多态(polymorphism) 指的是多种形式,因此函数多态允许函数可以有多种形式。术语函数重载(function overloading) 指的是可以有多个同名的函数,因此对名称进行了重载。两个术语指的是同一回事。
- 函数重载的关键是函数的参数列表——也称为函数特征标(function signature)。
- 编译器在检查函数特征标时,将类型引用和类型本身视为同一个特征标。
5. 函数模板
- C++模板函数具体化方法:
- 对于给定的函数名,可以有非模板函数、模板函数和显式具体化模板函数以及它们的重载版本。
- 显式具体化的原型和定义应以
template<>
打头,并通过名称来指出类型。 - 具体化将覆盖常规模板,而非模板函数将覆盖具体化和常规模板。
// 非模板函数
void Swap(job&, job&);
// 模板函数原型(其中class可以改用关键字typename)
template<class Any>
void Swap(Any&, Any&);
// 显式具体化模板函数
template<> void Swap<job>(job&, job&);
template<> void Swap(job&, job&); // 简化形式
- 记住,在代码中包含函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。
- 模板函数的实例化:
- 隐式实例化:模板函数调用导致编译器为特定类型生成函数定义时,得到的是模板实例(instantiation)。
- 显式实例化:直接命令编译器创建特定的实例。
template void Swap<int>(int, int); // 显式实例化
- 显式具体化声明在关键字
template
后包含<>
,而显式实例化没有。 - 警告:试图在同一个编程单元中使用同一种类型的显式实例和显示具体化将出错。
- 对于函数重载、函数模板和函数模板重载,C++需要(且有)一个定义良好的策略,来决定为函数调用使用哪一个函数定义,尤其是有多个参数时,这个过程称为重载解析(overloading resolution)。
- 创建候选函数列表。
- 使用候选函数列表创建可行函数列表。
- 确定是否有最佳的可行函数。
- 编译器根据参数匹配所需进行的转换决定最佳可行函数,从最佳到最差的顺序如下:
- 完全匹配,但常规函数优于模板。
- 提升转换(如
char
自动转换为int
)。
- 提升转换(如
- 标准转换(如
int
转换为char
,long
转换为double
)。
- 标准转换(如
- 用户定义的转换,如类声明中定义的转换。
- 通常,有两个函数完全匹配是一种错误,但这一规则有两个例外:
- 指向非
const
数据的指针和引用优先与非const
的指针和引用参数匹配。
- 指向非
- 常规函数优于模板函数。
- 完全匹配允许的无关紧要转换(其中Type为任意类型)
实参 | 形参 |
---|---|
Type | Type& |
Type& | Type |
Type[] | *Type |
Type(argument-list) | Type(*)(argument-list) |
Type | const Type |
Type | volatile Type |
Type* | const Type |
Type* | volatile Type* |
// 下面的原型都是完全匹配的
void recycle(blot);
void recycle(const blot);
void recycle(blot&);
void recycle(const blot&);
- 术语最具体(most specialized) 并不一定意味着显式具体化,而是指编译器腿短使用哪种类型时执行的转换最少。
- 用于找出最具体的模板的规则被称为函数模板的部分排序规则(partial ordering rules)。