C++ primer plus读书笔记——第8章 函数探幽
第8章 函数探幽
1. 对于内联函数,编译器将使用相应的函数代码替换函数调用,程序无需跳到一个位置执行代码,再调回来。因此,内联函数的运行速度比常规函数稍快,但代价是需要占用更多内存。
2. 要使用内联函数,需要在:
函数声明前加上关键字inline;
函数定义前加上关键字inline;
3. 程序员请求将函数作为内联函数时,编译器不一定会满足这种需求。它可能认为该函数过大或注意到函数调用了自己(内联函数不能递归)。
4. 必须在声明引用变量时进行初始化
int rat = 101;
int & rodents = rats;
C语言只能按值传递,按引用传递是C++新增的特性。
- 5. 如果程序员的意图是让函数使用传递给它的信息,而不对这些信息进行修改,同时又想使用引用,则应使用常量引用。
double refcube(const double &ra);
6. 当函数参数为常量引用时,什么时候创建临时变量呢?
有两种情况:
实参的类型正确,但不是左值;
实参的类型不正确,但可以转换为正确的类型。
double refcube(const double &ra)
{
return ra * ra * ra;
}
long edge = 5L;
double c5 = refcube(edge);
double c6 = refcube(7.0);
在上述两种情况下,编译器将生成一个临时匿名变量,并让ra指向它。这些临时变量只在函数调用期间存在,此后编译器便可以随意将其删除。
7. 对于形参为cosnt引用的C++函数,如果实参类型不匹配,则其行为类似于按值传递,为确保原始数据不被修改,将使用临时变量,该函数调用的参数的值传递给该匿名临时变量,并让参数来引用该变量。
8. 如果函数参数不是常量引用,而是普通引用,则实参不能是左值,而且必须类型严格匹配,否则编译出错。
double refcube(double &ra)
{
return ra * ra * ra;
}
double x = 3.0;
cout << refcube(x + 2.0);//错误,此时实参不能是左值
long a = 3;
cout << refcube(a);//错误,此时实参类型和形参不匹配
如果参数改成const double &ra则能够成功编译。为什么呢?P263
如果double refcube(double &ra)函数的意图是修改该变量ra,则创建临时变量将阻止该意图的实现,解决办法是禁止创建临时变量,现在的C++标准正是这么做的。而double refcube(const double &ra)
该函数的目的只是使用传递的值,而不是修改它们,因此临时变量不会造成任何不利的影响,反而会使函数在可处理的参数种类方面更加通用。
9. 将引用参数声明为常量数据的引用的理由有三个:
使用const可以避免无意中修改数据的编程错误;
使用const使函数能够处理const和非const实参,否则只能接受非const数据;
使用const引用使函数能够正确生成并使用临时变量,使函数能处理更多的参数种类。
10. 引入引用的目的是用于结构和类,而不是基本的数据类型。
11. P268为何将const用于引用返回类型
如果函数的返回类型为类型引用,则可以用作左值。这是因为赋值语句左边表达式必须返回一个可修改的内存块,而函数返回引用确实标识的是这样的一个内存块。
Accumulate(dup, five)= four;
而常规返回类型是右值。因为这样的返回值位于临时内存单元中,运行到下一条语句,它们可能不再存在。
假设您要使用引用返回值,但又不允许执行像给accumulate()赋值这样的操作,只需将返回类型声明为const引用。
12. 继承的另一个特征是,基类引用可以指向派生类对象,而无需进行强制类型转换。
13. ostream是基类,ofstream是派生类
14. 对于带参数列表的函数,必须从右向左添加默认值。也就是说,也为某个参数设置默认值,则必须为它右边所有的参数提供默认值。
int harpo(int n, int m = 4, int j = 5);
实参必须从左到右依次赋给相应的形参,而不能跳过任何参数。
Beeps = harpo(3, , 8);是不允许的。
15. 函数重载也称为函数多态。函数重载的关键是函数的参数列表(即函数特征标)。编译器在检查函数特征标时,将把类型引用和类型本身视为同一个特征标。
16. C++如何跟踪每一个重载的函数呢?它根据函数原型中指定的形参类型对每个函数名进行名次修饰(name decoration)。
17. 在C++98添加关键字typename之前,C++使用关键字class来创建模板。
18. P285~P288显示具体化。
对于给定的函数名,可以有非模板函数、模板函数、显式具体化目标函数以及它们的重载版本。
显式具体化的原型和定义应以template<>打头,并通过名次来指出类型。
具体化优先于常规模板,而非模板函数优先于具体化和常规模板。
19. 为进一步了解模板,必须理解术语实例化和具体化P288。
20. 在代码中包含函数模板本身并不会生成函数定义,他只是一个用于生成函数定义的方案。
21. template <typename T>
void Swap(T &a, T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}
int i = 10, j = 20;
隐式实例化 swap(i, j)
显示实例化template void swap<int>(int &, int &);直接命令编译器创建特定的实例,将使用swap模板生成一个使用int类型的实例。
显式具体化使用下面两个等价的声明之一:
template <> void swap<int>(int &, int &);
template <> void swap(int &, int &);
该显式具体化的意思是”不要使用swap()模板来生成函数定义,而应使用专门为int类型显式地定义的函数定义”。这两个原型必须有自己的函数定义。
显式具体化声明在template后包含<>,而显式实例化没有。
22. 隐式实例化、显式实例化和显式具体化统称为具体化。
23. 8.5.6模板函数的发展P295-P297
C++ 11关键字decltype
后置返回类型(trailing return type)