概述
函数探幽
目录:
- C++内联函数
- 引用变量
- 默认参数
- 函数重载
- 函数模板
一、C++内联函数:编译器将使用相应的函数代码替换函数调用。内联函数的运行速度比常规函数稍快,但需要占用更多内存。
方法:在函数声明(定义)前加上关键字inline
通常省略原型,将整个定义(即函数头和所有函数代码)放在本应提供原型的地方。
二、引用变量
1.必须在声明引用时将其初始化
引用更接近const指针:int & rodents = rats; //int * const pr = &rats;
2.引用常被用作函数参数,使得函数中的变量名成为调用函数中的变量的别名,即按引用传递,它允许被调函数能够访问调用函数中的变量。
只能通过原型或函数定义才能知道其是否是按引用传递的。
3.若引用参数是const,则编译器将在以下情况生成临时变量:实参类型正确,但不是左值;实参类型不正确,但可转换为正确的类型。
a. 左值参数是可被引用的数据对象,如变量、数组元素、结构成员,引用和解除引用的指针都是左值。非左值包括字面常量(用引号括起的字符串除外,其由地址表示)和包含多项的表达式。
b. 如果函数调用的参数不是左值或与相应的const引用参数的类型不匹配,则C++将创建类型正确的匿名变量,将函数调用的参数的值传递给该匿名变量,并让参数来引用该变量。
c. 将引用参数声明为常量数据的引用:使用const可避免无意中修改数据的编程错误;使用const使函数能够处理const和非const实参,否则将只能接受非const数据;使用const引用使函数能正确生成并使用临时变量。因此,应尽可能将引用形参声明为const。右值可指向右值,它让库设计人员能够提供有些操作的更有效实现。
4 . 返回引用的函数实际上是被引用的变量的别名。返回引用时,应避免返回函数终止时不在存在的内存单元引用,同样,也应避免返回指向临时对象的指针。
避免以上问题的方法:返回一个作为参数传递给函数的引用,用new来分配新的内存空间。常规(非引用)返回类型是右值—不能通过地址访问的值,这种返回值位于临时内存单元中,运行到下一条语句时,它们可能不再存在。
5. string类定义了一种 char* 到string的转换功能,这使得可以使用C-风格字符串来初始化string对象,若函数形参类型为const string & , 在调用函数时,使用的实参可以是string对象或C-风格字符串,如引号括起的字符串字面值。以空字符结尾的char数组或指向char的指针变量。
6. 使得能够将特性从一个类传递给另一个类的语言特性叫继承。ostream是基类(ofstream建立在它之上),而ofstream是派生类(从ostream派生而来),派生类继承了基类的方法,这意味着ofstream对象可以使用基类的特性,如格式化方法precision( )和setf( )。继承的另一个特征是,基类引用可以指向派生类对象,而无需进行强制类型转换。即可以定义一个接受基类引用作为参数的函数,调用该函数时可将基类对象或派生类对象作为参数。
7. 使用引用参数的主要原因:能修改调用函数中的数据对象;通过传递引用而不是整个数据对象,可提高程序运行速度。
a.对于使用传递的值而不作修改的函数:若数据对象很小(内置数据类型,小型结构),则按值传递;若数据对象为数组,则将指针声明为指向const的指针;若是较大的结构,则使用const指针或const引用(提高效率);若数据对象是类对象则用const引用。传递类对象参数的标准方式是按引用传递。
b. 对于修改调用函数中数据的函数:若数据对象是内置数据类型,则使用指针;若是指针,则只能使用指针;若是结构,则使用引用或指针;若数据对象是类对象,则使用引用。
三、 默认参数
1. 默认参数指当函数调用中省略了实参时自动使用的一个值
2. 设置默认值必须通过函数原型。由于编译器通过查看原型来了解函数所使用的参数数目,因此函数原型也必须将可能的默认值告知程序,方法是将值赋给原型中的参数。
3. 对于带参数列表的函数,必须从右向左添加默认值。只有原型指定了默认值,函数定义与没有默认参数时完全相同。
四、 函数重载
C++允许定义名称相同的函数,条件是它们的特征标(参数列表)不同。若参数数目和/或参数类型不同,则特征标也不同。编译器在检查函数特征标时,将把类型引用和类型本身视为同一特征标。是特征标,而不是函数类型使得可以对函数进行重载。仅当函数执行基本相同的任务,但使用不同形式的数据时,才应采用函数重载。
五、 函数模板 template <typename T>
1. 模板允许以泛型(而不是具体类型)的方式编写程序,因此也被称为通用编程,由于类型是用参数表示的,因此模板特性有时也被称为通用编程。由于类型是用参数表示的,因此模板特性有时也被称为参数化类型;函数模板允许以任意类型的方式来定义函数。关键字template、typename(class)、尖括号是必须的。
如果需要多个将同一种算法用于不同类型的函数,请使用模板。程序最终的代码不包括任何模板,而只包含为程序生成的实际函数。
应将模板放在头文件中,并在需要使用模板的头文件中包含该头文件。
2. 可以像重载常规函数定义那样重载模板定义,被重载的模板的函数特征标必须不同。
并非所有的模板参数都必须是模板参数类型。如:
void Swap (T &a, T &b);
void Swap (T *a, T *b, int n);
3. 显式具体化(具体化函数定义),包括所需代码,当编译器找到与函数调用匹配的具体化定义时,将使用该定义而不再寻找模板。
标准方法:对于给定的函数名,可以有非模板函数、模板函数和显式具体化模板函数以及其重载版本;显式具体化的原型和定义应以template < >打头,并通过名称来指出类型;非模板函数优先于具体化,具体化优先于常规函数。
下面为用于交换job结构的非模板函数、模板函数和具体化的原型:
(1). void Swap (job &, job &); //非模板
(2). template <typename T> //模板
void Swap (T &, T &);
(3). template< > void Swap <job> (job &, job &); //具体化,<job>可选
4. 在代码中包含函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成函数定义时,得到的是模板实例。模板并非函数定义,但使用特定类型(如int)的模板实例是函数定义,这种实例化方式被称为隐式实例化。
a.显式实例化,即直接命令编译器创建特定的实例。其语法是,声明所需的种类—用< >符号指示类型,并在声明前加template.
template void Swap <int> (int ,int);
b.相对显式具体化两种方法:
template < > void Swap <int> (int &, int &);
template < > void Swap (int &, int &);
这些原型必须有自己的函数定义,显式具体化前缀为template < >,而显式实例化没有< >。试图在同一个文件(或转换单元)中 使用同一种类型的显式实例化和显式具体化将出错。
隐式实例化、显式实例化和显式具体化统称为具体化,它们表示的都是使用具体类型的函数定义,而不是通用描述。
5. 对于函数重载、函数模板和函数模板重载,C++需要(且有)一个定义良好的策略来决定为函数调用使用哪一个函数定义,尤其是有多个参数时,这个过程称为重载解析。
第一步 创建候选函数列表(与被调用函数名称相同的函数和模板函数)
第二步 使用候选函数列表创建可行参数列表(只考虑特征标,不考虑返回类型)。这些都是参数数目正确的函数,为此有一个隐式转换序列,其中包括实参类型与相应的形参类型完全匹配的情况。
第三步 确定是否有最佳的可行函数。若有则使用它,否则该函数调用出错。对于两个完全匹配的函数:非模板函数优先于模板函数,两个模板函数中较具体的模板函数优先,即显式具体化优于使用模板隐式生成的具体化。
用于找出最具体的模板的规则被称为函数模板的部分排序规则。
总之,重载解析将寻找最匹配的函数。若只存在一个这样的函数则选择它;若存在多个这样的函数,但其中只有一个是非模板函数,则选择该函数;若存在多个适用的函数且都为模板函数,则选择最具体的那个;若有多个同样合适的模板函数或非模板函数,但没有一个比其他函数更具体,则函数调用将是不确定的,因此是错误的。
6. 关键字decltype
decltype (expression) var ;
遍历核对表顺序:
(1)、若expression是一个没有用括号括起的标识符,则var的类型与该标识符类型相同,包括const等限定符。
(2)、若expression是一个函数调用,则var类型与该函数的返回类型相同(编译器通过查看函数的原型来获悉返回类型,而无需实际调用)
(3)、若expression是一个左值,则var为指向其类型的引用
(4)、若前面条件都不满足,则var类型与expression类型相同
若需多次声明,可结合使用typedef和decltype,如:
template <class T1 , class T2>
void ft(T1 x , T2 y)
{
……
typedef decltype (x + y) xytype ; //xytype为(x+y)类型的别名
xytype xpy = x + y ;
xytype arr [10] ;
xytype & rxy = arr [2] ; //rxy为引用(与arr[2]等价)
……
}
7. auto语法
auto h (int x , float y) ->double ; //这将返回类型移到参数声明后面。 ->double被称为后置返回类型。其中auto是一个占位符,表示后置返回类型提供的类型。
即:
template <class T1, class T2>
auto gt(T1 x, T2 y) ->decltype(x+y)
{
……
return x+y ;
}
最后
以上就是阳光招牌为你收集整理的函数进阶(函数模板、重载解析)的全部内容,希望文章能够帮你解决函数进阶(函数模板、重载解析)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复