函数探幽
- 内联函数
- c++为提高程序运行速度的一项改进。
- 非内联函数在调用时来回跳跃并记录产生了一定的开销。
- 内联函数编译器将使用相应的函数代码替换函数调用。无需跳到另一个位置去执行代码。
- 内联函数运行速度比常规函数稍快,但代价是占用更多内存。
- 如果程序在10个不同的地方调用同一个内联函数,该程序将包含该函数代码的10个副本。
- 如果执行函数代码时间比函数调用时间短,则应该使用内联函数。
- 要使用内联函数需要在函数声明和定义前加上inline关键字。
- 通常的做法是省略声明,直接定义。
- 有些函数即使声明了inline编译器也不一定会将其作为内联函数,可能的原因有函数过大或函数调用了自己。
- 有些编译器没有实现内联函数。
- 内联与宏
- 内联代码的原始实现是宏
- 宏不能按值传递
- #define SQUARE(X) X*X
- int c = 13
- a = SQUARE(c++)
- 则替换后的结果为a = c++*c++;
- 将c递增了两次。
- 但使用内联函数就只会递增一次。
- 所以推荐使用内联函数。
- 引用变量
- 引用是一定义的变量的别名。
- 引用变量的主要用途是用作函数的形参。
- 通过使用引用变量作为参数,函数将使用原始数据,而不是副本。
- 创建引用变量
- &符号
- int rats;
- int & rodents = rats;
- int &的意思是指向int的引用。
- rodents和rats指向相同的值和内存单元。
- 注意区别地址运算符和引用
- 指针和引用的区别之一
- 引用在声明是必须初始化,而指针不需要。
- 引用更接近const指针。一旦与某个变量关联起来,就将一直效忠与他。类似于int * const pr = &rats;
- 只能通过初始化声明来设置引用,但不能通过赋值来设置。
- 引用变量的特别之处
- 如果只是想使用变量中的数据,那么引用参数应该使用常量引用。如const double & a;
- 在参数数据比较大时,引用参数很有用。
- 按值传递的参数可使用多种类型的实参。
- double z = cube(x+2.0);
- z = cube(8.0);
- int k = 10;
- z = cube(k);
- double yo[3] = {2.2, 3.3, 4.4};
- z = cube(yo[2]);
- 上述参数传递给接受引用参数的函数时,现代c++中这是错误的。在较老的编译器中会发出警告。
- 早期c++允许将表达式传递给引用变量。
- 具体方式是创建应该临时变量。
- 临时变量
- 如果实参与引用参数不匹配。c++将生成临时变量。
- 现在只有参数为const引用时c++才允许,但以前不是。
- 实参类型正确,但不是左值。非左值有表达式,字符串常量等。
- 实参类型不正确,但可以转换为正确类型。
- 现在常规变量和const变量都可被视为左值。因为可以通过地址访问他们。
- 这些临时变量只在函数调用期间存在,此后编译器将随意将其删除。
- 在现在的大多数c++编译器这种行为只对于常量引用可行。
- 在早期c++较宽松时可以接受不是常量引用而创建临时变量,但达不到修改引用的目的。
- 尽可能多的使用const
- 1 使用const可以避免无意中修改数据的错误。
- 2 const可使能够处理const和非const实参,否则只能接受非const数据。
- 3 const引用使函数能够正确生成并使用临时变量。
- c++11新增了另一种引用——右值引用
- 使用&&声明
- 使用右值引用实现移动语义。后面讲。
- 为什么要返回引用
- 如果不是返回引用,那么函数将返回结果复制到应该临时位置,再将临时位置中的内存拷贝到dup中。但返回值为引用时,直接把team复制到dup中。省去临时位置的步骤。
- 避免返回指向临时变量的指针和引用。因为函数结束后临时变量将不存在。
- 两种方法避免
- 返回一个作为参数传递给函数的引用。
- new来分配新的存储空间。
- 但new来分配会存在一个释放内存的问题。auto_ptr 模板和c++11新增的unique_ptr可帮助程序员自动完成释放工作。16章介绍。
- 假设要使用一个函数返回的引用,而不是给返回的引用赋值。那么应该将返回的引用声明为const引用。
- 对象继承和引用
- 基类引用可以指向派生类对象,无需进行强制类型转换。
- 何时使用引用参数。
- 对于使用传递的值而不修改的函数
- 如果数据对象很小,如内置的数据类型或小型结构,则按值传递。
- 如果数据对象是数组,则使用指针,并将指针声明为const指针。
- 如果数据对象比较大,则使用const指针或const引用,提高效率。
- 如果数据对象是类对象,则使用const引用。传递类对象的标准方式是按引用传递。
- 对于修改调用函数中数据的函数
- 如果数据对象是内置数据类型,则使用指针。
- 如果数据对象是数组,则使用指针。
- 如果数据对象是结构,使用指针或引用。
- 如果数据对象是类,则使用引用。
- 对于使用传递的值而不修改的函数
- 默认参数
- 如何设置默认参数。
- char * left(const char * str, int n = 1);
- 为某个参数设置默认值,则必须为它右边的所有参数提供默认值。
- 只有原型也就是函数声明指定了默认值,函数定义与没有默认参数时完全相同。
- 函数重载
- 可以通过函数重载来设计一系列函数,他们完成相同的工作,但使用不同的参数列表。
- 如果参数数目不同或参数类型不同,则特征标也不同。但是一些看起来彼此不同的特征标是不能共存的。
- 在函数调用时,将调用与函数最匹配的那一个。如果最匹配的没有,那么调用第二匹配的。以此类推。
- 名称修饰(名称矫正)
- 它根据函数原型中指定的形参对每个函数名进行加密。
- 编译器将名称转换为不太好看的内部表示。
- 对参数数目和类型进行编码。添加一组符号随特征标而异。
- 函数模板
- 函数模板是通用函数的描述。
- 他们使用泛型来定义函数。
template <typename T>//也可以使用template <class T> void Swap(T &a, T &b) { T = temp; temp = a; a = b; b = temp; }
- 为特定类型提供具体化的模板定义。
- 显示具体化不会根据模板生成函数定义,而是使用对特定类型定义的专用函数定义。
- template<> void Swap(job &j1, job &j2);
- 实例化
- 代码中包含函数模板本身并不会生成函数定义。只是一个生成函数定义的方案。
- 编译器为特定类型生成函数定义时得到的是模板实例。这种实例化方法被称为隐式实例化。例如直接调用Swap(a, b);
- 最初编译器只能通过隐式实例化,来生使用模板生成函数定义。
- 现在c++还允许显示实例化。
- template void Swap< int >(int, int);
- 还可以在函数中创建显示实例化
- Swap< double >(a, b);
- 编译器使用哪个函数版本
- 第一步:创建候选列表。其中包含与被调函数的名称相同的函数和模板函数。
- 第二步:使用候选列表创建可行函数列表。
- 第三步:检测是否有最佳的可行函数。如果有使用它,如果没有函数调用出错。
原文地址:https://www.cnblogs.com/yangzixiong/p/11960867.html
时间: 2024-10-21 10:34:37