程序编译之后由一组机器语言指令组成,运行程序时将其载入内存,运行函数时会跳转到函数所在的内存区域,执行完成后返回原来内存区域
该跳转过程会有一个时间,虽然很短
内联函数可以让函数就写在调用函数的位置,从而不需要跳转函数,但是可能会增加内存的占用。
当函数执行的时间小于跳转时间时,使用内联函数可以明显减少程序运行时间。否则减少的运行时间不明显。
如果要使用该特性,必须采取以下措施之一:
通常的做法是省略原形,将整个定义放在本应该提供原形的地方。
编译器不一定会满足内联函数的要求,因为它可能认为该函数过大,或者函数调用了自己(内联函数不能递归);更或者有些编译器没有启用该特性
可以代替c语言中的宏定义
引用是已定义变量的别名。
& 符号可以用的指示变量的地址。如果要讲rodents作为rats变量的别名,则:
int rats;
int & rodents = rats;
上述引用声明允许将rats和rodents互换————他们指向相同的值和内存单元。引用必须在声明引用时将其初始化,不能像指针那样。
int rat;
int &rodents = rats;
int * const pr = &rats;
rodents 扮演的角色与表达式*pr相同。
这种传递方式被称为按引用传递。C语言中只能按值传递。
如果不想要程序修改引用的值
double refcube(const double &ra);
但是这样的代码完全可以用按值传递代替,当数据比较大的时候(例如结构和类),引用参数会很有用。
如果实参和引用参数不匹配,C++将生成临时变量(仅当参数为const引用时)。
应尽可能使用const
引用非常适合用于结构和类。
假设有一个结构定义如下:
struct free_throws{
std::string name;
int made;
int attempts;
float percent;
}
则可以定义以下的函数原型去引用他
void set_pc(free_throws & ft);
引用可以作为函数声明,例如:
free_throws & accumulate(free_throws & target,const free_throws & source);
返回引用时最重要的一点是:
const free_throws & clone2(free_throws & ft){
free_throws newguy;
newguy = ft;
return newguy;
}
令一种方法是使用new来分配新的存储空间。
const free_throws & clone(free_throws & ft){
free_throws *pt = new free_throws;
*pt = ft;
return newguy;
}
不过这样的代码在后续的代码中很容易忘记对new的内存调用delete.
可以加char*类型赋值给string &,如果如此传参,就会产生临时变量
默认参数指的就是当函数调用中省略了实参时自动使用的一个值。
可以只在函数定义的时候写出:
char *left(const char *str, int n = 1);
又函数多态,是C++在c语言的基础上新增的功能。
函数重载的关键是函数列表————也称为函数特征标(function signature)。如果两个函数的参数数目和类型相同,同时参数的排列顺序也相同,
则它们的特征标相同,而变量名是无关紧要的。
C++允许定义名称相同的函数,条件是他们的特征表不同。如果参数数目和/或参数的类型不同,则特征标也不同。例如:
void print(const char * str, int width);
void print(double d, int width);
void print(long l, int width);
如果函数的调用没有原形匹配,那么C++会尝试使用标准类型转换强制进行匹配。(int x 和 int &x的函数标是相同的),编译器在检查函数特征标时,会吧类型引用和类型本身视为同一个特征标。
void dribble(char *bits);
void dribble(const char *cbits);
函数模板允许使用泛型来编写程序。有时也称为通用编程。
template
void Swap(AnyType &a,AnyType &b){
AnyType temp;
temp = a;
a = b;
b = temp;
}
代码的第一行表示创建一个模板,并将类型命名为AnyType。两个关键字是必须的,其中typename可以使用class,两者等价。
在使用函数时,如果传入的参数是int,则会将AnyType转换为int
如果写先声明后给出函数,则必须在声明之前和函数之前都加上模板。
模板同样可以重载。
struct job{
char name[40];
double salary;
int floor;
};
void Swap(job &,job &);
template
void Swap(T &,T &);
template <> void Swap(job &, job &);
代码中包含函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。
编译器在使用模板为特定类型生成函数定义时,得到的是模板实例。
这种实例化的方式被称为隐式实例化。
显示实例化:
template void Swap(int,int);
显示具体化:
template <> void Swap(int &,int &);
template <> void Swap(int &,int &);
区别在于templage后面是否有<>符号,显示具体化的意思是“不要使用Swap()模板来生成函数定义”。
在同一个文件中使用同一种类型的显式实例和显式具体化将出错。
template
void Swap(T &,T &); //模板
template <> void Swap(job &, job &); // job的显示具体化
int main(void){
template void Swap;
short a,b;
...
Swap(a,b); //隐式实例化
job n, m;
...
Swap(n,m); //显示具体化后实例化
char g, h;
...
Swap(g, h); //显示实例化
...
}
三种具体化: 隐式实例化、显示具体化和显式实例化
对于函数重载、函数模板和函数模板重载,C++有一个策略。
该过程称为重载解析
选用函数的顺序:
如果有两个函数完全匹配,是一种错误。此时编译器可以会出现“ambiguous”的错误。
但是有两个例外:
指向非const数据的指针和引用优先与非const指针和引用参数匹配。
例如:
void recycle(blot &); // #1
void recycle(const blot &); // #2
如果实参是blot类型的,则优先使用1(非引用和指针的const和非const之间的区别不适用)
如果一个是非模板函数,另一个不是。该情况下,非模板函数将优于模板函数。
如果两个都是模板函数,则较具体的模板函数优先。
用于找出最具体的模板的规则被称为 函数模板的部分排序规则 这是C++98的特性
template < typename T>
void a1(T x); //#1
template < typename T>
void a1(T * x); //#2
...
int x;
a1(x); //调用#1
a1(&x); //调用#2
...
调用模板的时候在函数后跟上,优先使用模板函数
int x;
decltype(x) y; //创建一个y和x类型相同
decltype(expression) var;
步骤:
如果expression是一个没有用括号括起来的标识符,则var的类型与改标识符的类型相同,包括const等。
double x = 5.5;
double y = 7.9;
double &rx = x;
const double *pd;
decltype(x) w; // w是double
decltype(rx) u = y; //u是double &
decltype(pd) v; // v是 const double *
如果expression是一个函数调用,则var的类型与函数的返回类型相同。
如果expression是一个左值,则var为指向其类型的引用。
没有带括号的左值在第一步时已经被处理,所以引用需要带括号。
double xx = 3.3;
decltype((xx)) r2 = xx; // r2是double &
括号并不会改变左值性
如果以上的标间都不满足,则var的类型与expression的类型相同(表达式或数值一类的)。
template
?type? gt(T1 x,T2 y);
如果要是一个模板函数的返回值无法确定,则可以使用后置返回类型
double h(int x,float y);
auto h(int x, float y) -> double;
上述两句等价,也可以如下定义:
template
auto gt(T1 x, T2 y) -> decltype(x+y);