内联函数
常规函数调用使程序跳到另一个地址(函数的地址),并在函数结束后返回。来回跳跃并记录跳跃位置意味着一定的开销。 内联函数使得编译器将相应的函数代码替换函数调用。程序无需跳到另一个位置处执行代码,再调回来。 因而,内联函数运行速度比常规函数快,但是代价是需要占用更多的内存。如果程序在10个不同的地方调用调用同一个内联函数,则该程序将包含该函数代码的10个副本。
使用内联函数:
在函数声明前加上关键字inline
在函数定义前加上关键字inline
通常的做法是省略原型,将整个定义(即函数头和所有函数代码)放在本应提供原型的地方。
示例代码:
inline.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <iostream> using namespace std ;inline double square (double x) {return x * x;}int main () { double a, b; a = square(5.0 ); b = square(4.5 + 7.5 ); cout << "a = " << a << ", b = " << b <<endl ; return 0 ; }
执行结果: a = 25, b = 144
NOTE: C语言使用预处理语句**#define
提供宏(内联代码的原始实现),比如:#define SQUARE(X) X*X
这是通过文本替换来实现的。
引用变量标识符&
引用变量的主要用途是用作函数的形参。通过将引用变量用作参数,函数将使用原始数据,而不是副本。这样除了指针以外,引用也为函数处理大型结构提供了方便的途径。
创建引用变量 1 2 int rats;int & rodents = rats;
上述的引用声明允许将rats和rodents互换,他们指向相同的值和内存单元
。示例代码:
firstref.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> using namespace std ;int main () { int rats = 101 ; int & rodents = rats; cout << "rats = " << rats << ", rodents = " << rodents <<endl ; rodents++; cout << "After rodents++, rats = " << rats << ", rodents = " << rodents << endl ; cout << "Address &rats = " << &rats << ", &rodents = " << &rodents <<endl ; return 0 ; }
执行结果:
1 2 3 rats = 101 , rodents = 101 After rodents++, rats = 102 , rodents = 102 Address &rats = 0x7ffe8c156f1c , &rodents = 0x7ffe8c156f1c
将引用用作函数参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 //按值传递 void lazy(int x); int main() { int times = 20; lazy(times); return 0; } void lazy(int x) { ... } //按引用传递 void work(int &x); int main() { int times = 20; work(times); } void work(int &x) { ... }
如果让函数使用传递给她的信息,而不对信息进行修改,同时又箱使用引用,则应使用常量引用
,即使用const。
double refcube(const double &ra);
将引用参数声明为常量数据的引用的理由三个:
使用const可以避免无意中修改数据的编程错误
使用const使函数能够处理const和非const实参,否则将只能接受非cosnt数据
使用const引用使函数能够正确生成并使用临时变量
C++11新增了另一种引用 – 右值引用
,这种引用可以指向右值,是使用&&
声明的。目的用来实现移动语义。而’&’引用是左值引用。
1 2 3 double && rref = std ::sqrt (36.00 ); double j = 15.0 ;douvle && jref = 2.0 * j + 18.5 ;
合适使用引用参数 使用引用参数的主要原因有两个:
能够修改调用函数中的数据对象
通过传递引用而不是整个数据对象,提高程序运行速度
对于使用传递的值而不做修改的函数:
如果数据对象很小,如内置数据类型或小型数据。则按值传递
如果数据对象是数组,则使用指针,并将指针声明为指向const的指针
如果数据对象是较大的结构,则使用const指针或const引用,提高效率,节省复制结构所需的时间和空间
如果数据对象是类对象,则使用const引用,传递类对象参数的标准方式是按引用传递
对于修改函数中数据的函数:
如果数据对象是内置数据类型,则使用指针。例如调用fixit(&x)这样的函数,则很明显要修改x
如果数据对象是数组,则只能使用指针
如果数据对象是结构,则使用引用或者指针
如果数据对象是类对象,则使用引用
默认参数
默认参数
指的是当函数调用中省略了实参时自动调用的一个值。例如将void wow(int n)
设置成n有默认值是1,则函数调用wow()
就等价于wow(1)
通过函数原型设置默认值:char * left(const char *str, int n = 1);
函数重载
同名的函数使用不同的参数列表。 因而关键是参数列表,也称作函数特征标
。如果两个函数的参数数目和类型相同,同时参数的排列顺序相同,则他们的特征标相同
。返回类型可以不同,但是特征标也必须不同
1 2 3 4 5 6 7 8 9 10 11 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) ;long gronk (int n, float m) ;double gronk (int n, float ) ; double gronk (float n, float m) ;
因而在使用print()
函数时,编译器根据采取的用法使用有相应特征标的函数原型。
1 2 3 print (1999.0 , 10 );print (1999L , 15 );print ("pen" , 12 );
当函数基本上执行相同的人物,但使用不同形式的数据时,才应采用函数重载。
名称修饰(或名称矫正) 根据函数原型中指定的形参类型对每个函数名进行加密。long MyFun(int, float);
编译器将其转化为内部表示来描述接口:?MyFun@@YAXH
函数模板
函数模板是通用的函数描述,使用泛型来定义函数,其中泛型可以用具体的类型(如int或double)替换。 通过将类型作为参数传递给模板,可以使编译器生成该类型的函数 由于模板允许以泛型的方式编写程序,因此也称作通用编程
由于类型是用参数表示的,因此模板特性有时也称作参数化类型
函数模板允许以任意类型的方式来定义函数。例如,可以建立一个交换模板:
1 2 3 4 5 6 7 8 9 template <typename AnyType>void swap (AnyType &a, AnyType &b) { AnyType temp; temp = a; a = b; b = temp; }
第一行建立一个模板,将类型命名为AnyType,关键字template
和typename
是必需的,除非使用另一个关键字class
代替typename
(这两个关键字等价的)。另外,必须使用尖括号。 类型名可以任意选择(此处是AnyType),常用T
如果需要多个将同一个算法用于不同类型的函数,请使用函数模板。如果不考虑向后兼容的问题,并愿意键入较长的单词,则声明类型参数时,应使用关键字typename而不使用class。
Example:
funtemp.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <iostream> using namespace std ;template <typename T> void Swap (T &a, T &b) ; template <typename T> void Swap (T &a, T &b) { T temp; temp = a; a = b; b = temp; } int main () { int i = 10 ; int j = 20 ; cout << "Before swap, i = " << i << ",j = " << j <<endl ; Swap(i, j); cout << "After swap, i = " << i << ",j = " << j <<endl ; double x = 24.45 ; double y = 45.2 ; cout << "Before swap, x = " << x << ",y = " << y <<endl ; Swap(x, y); cout << "After swap, x = " << x << ",y = " << y <<endl ; return 0 ; }
Result:
1 2 3 4 Before swap, i = 10 ,j = 20 After swap, i = 20 ,j = 10 Before swap, x = 24.45 ,y = 45.2 After swap, x = 45.2 ,y = 24.45
Note: 函数模板不能缩短可执行程序,最终仍将由独立的函数定义,最终的代码不包含任何函数模板,而只包含了为程序生成的实际函数。
使用函数模板的好处是使得多个函数定义更简单可靠。
重载的函数模板
可以像重载常规函数定义一样重载函数模板定义。保证被重载的函数模板特征标
必须不同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 template <typename T>void Swap (T &a, T &b) ;template <typename T>void Swap (T *a, T *b, int n) ;template <typename T>void Swap (T a[], T b[], int n) { T temp; for (int i = 0 ; i < n; i++) { temp = a[i]; a[i] = b[i]; b[i] = temp; } }
局限性
模板函数可能无法处理某些类型。例如数组、指针、结构的某些运算。 一种解决方案是C++允许你重载运算符+,以便能够将其用于特定的结构或类; 另一种是为特定类型提供具体化的模板定义。
显示具体化 C++98标准选择了以下的方法实现第三代具体化:
对于给定的函数名,可以有非模板函数、模板函数、显示具体化模板函数、以及他们的重载版本
显示具体化的原型和定义应该以template <>
开头,并通过名称指出类型
优先级: 非模板函数 > 显示具体化模板函数 > 常规模板函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct job { char name[40 ]; double salary; int floor ; }; void Swap (job &, job &) ;template <> void Swap<job>(job & ,job &);template <typename T>void Swap (T &, T &) ;
Swap<job>
的<job>
是可选的,因为函数的参数类型声明,这是job的一个具体化。因此,该原型也可以写作:template <> void Swap(job &, job &);
Example:
twoswap.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <iostream> using namespace std ;template <typename T>void Swap (T &a, T &b) ;struct job { char name[40 ]; double salary; int floor ; }; template <> void Swap<job>(job &j1, job &j2);void Show (job &j) ;template <typename T>void Swap (T &a, T &b) { T temp; temp = a; a = b; b = temp; } template <> void Swap<job>(job &j1, job &j2) { double t1; int t2; t1 = j1.salary; j1.salary = j2.salary; j2.salary = t1; t2 = j1.floor ; j1.floor = j2.floor ; j2.floor = t2; } void Show (job &j) { cout << j.name << " : $" << j.salary << " on floor " << j.floor <<endl ; } int main () { cout .precision(2 ); cout .setf(ios::fixed, ios::floatfield); int i = 10 , j = 20 ; cout << "Before swap, i = " << i << ",j = " << j <<endl ; Swap(i, j); cout << "After swap, i = " << i << ",j = " << j <<endl ; job sue = {"Susan" , 7300.60 , 7 }; job sidney = {"Sidney Taffee" , 78060.72 , 9 }; cout << "Before job swap,\n" ; Show(sue); Show(sidney); Swap(sue, sidney); cout << "After job swap,\n" ; Show(sue); Show(sidney); return 0 ; }
Results:
1 2 3 4 5 6 7 8 Before swap, i = 10 ,j = 20 After swap, i = 20 ,j = 10 Before job swap, Susan : $7300.60 on floor 7 Sidney Taffee : $78060.72 on floor 9 After job swap, Susan : $78060.72 on floor 9 Sidney Taffee : $7300.60 on floor 7
关键字decltype(c++11) 1 2 3 4 5 6 template <class T1 , class T2 >void ft (T1 x , T2 y ) { ... ?type? xpy = x + y; ... }
此处的xpy不知道如何确定类型,在C++11中提供关键字decltype
,使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int x;decltype (x) y;decltype (x+y) xpy;xpy = x + y; decltype (x+y) xpy = x + y;decltype (auto ) a;template <class T1 , class T2 >void ft (T1 x , T2 y ) { ... decltype (x+y) xpy = x + y; ... }
C++11后置返回类型声明语法 1 2 3 4 template <class T1 , class T2 >?type ? gt (T1 x , T2 y ) { return x + y; }
此处无法确定返回的类型,因为未声明参数x和y,所以他们不再作用域呢,无法使用decltype关键字(必须声明参数后使用)。C++11新增了一种语法:
double h(int x, float y);
使用新增的语法后可以这样编写:
auto h(int x, float y) -> double;
这样将返回类型已到了参数声明之后,->double
被称为后置返回类型
。
因而使用这种方法声明模板函数:
1 2 3 4 template <class T1 , class T2 >auto gt (T1 x , T2 y ) -> decltype (x +y ) { return x + y; }