第八章
内联函数:
inline double square(double x) {return x*x};
引用:
必须在声明引用的同时将其初始化,而不能像指针那样,先声明再赋值:
int a;
int & red;//错误!!极端错误!声明时必须先初始化。
red = a;
所以这样看起来,引用更像const指针,必须在创建时进行初始化,一旦与某个变量关联,必须一直捆绑,不能再关联其他。
函数参数为引用时,如果不改变参数值,应当尽可能的将形参声明为const引用:
1、首先能防止因外修改原数据
2、const形参的函数能够处理const和非const实参,否则只能接受非const数据。
3、const引用使函数能够正确生成并使用临时变量。
在函数返回时,切勿返回指向函数中临时变量的引用或者指针!!因为引用相当与别名,指针相当于存放变量的地址。一旦函数结束后,存储临时变量的地址和临时变量本身都将被释放掉,没有了。
解决方法一般是返回传入函数的实参的引用,这样返回的引用也就指向了这些数据,只要实参进来了,这些数据肯定是有的,不会消失。
而返回传入实参的引用时,也应该注意,尽量将返回的引用也设为const类型,加入不改变原实参的话。
const my_type& accumulate(const my_type& a);//函数原型
因为如果不加的话,有可能误写如下函数,导致原参数改变:
accumulate(a) = b;
因为返回的是原实参的引用,所以,整个accumulate(a)调用之后,就是a的一个引用,很明显,一个参数的引用是可以被赋值的。如果返回是加上了const,则上语句就会报错,不允许这么做!可见处处是坑啊
默认参数:
参数的默认值只在函数原型中设定。要为某个参数设置默认值,则它右边的所有参数都必须提供默认值!不能跳着来,随便设定哪个参数有默认值
int harpo(int n, int m=4, int j=5);//没毛病
int chico(int n, int m=4, int j);//不行,不能单独中间的某个参数有默认值,后面没了。
而且实参按照从左到右的顺序一次被赋值给相应的形参,不能跳过任何参数
haipo(3, ,8);//这样是不行的!!不要想当然中间那个不是有默认值4麽?!!!不行。
函数重载
简单讲,函数名称相同,特征标不同,即为函数重载。
编译器在检查函数特征标时,将类型引用和类型本身视为同一个特征标。
double cube(double x);
double cube(double & x);//这俩搞一起会报错,因为不是重载,有歧义性。
double a = 3.0;
cube(a);//因为在这种调用的情况下,就有歧义了。
函数重载一般用于执行相同的任务,但是使用不同形式的数据是,才会用函数重载。并不什么场合都乱用。
函数模板
先帖一下典型用法:
#include<iostream>
template<typename T>
void swap(T& a, T& b);//这里注意一下,这句跟上方的类型定义T其实是一个语句,但是分两行写,可以发现,上一句没有结束分号。
int main
{
int a = 3;
int b = 4;
swap(a, b);
double c = 5.0;
double d = 6.0;
swap(c, d);
return 0;
}
template<typename T>
void swap(T& a, T& b)
{
T = temp;
temp = a;
a = b;
b = temp;
}
当然模板也能重载,就是多来几个参数列表不同的模板就是了。
下面引出函数模板中比较绕的两个名词:
具体化与实例化。
从头开始。我们最初想到用模板,就是需要对多种不同类型的的数据使用同一种算法时,使用模板,减少因类型引起的重复工作,说白了就是为了省劲。比如上面的交换函数,如果不用模板,俩int我得定义一个函数,俩double我得定义一个函数,然后short,long,float等等,没完了。。。所以就想出了模板。但是随着使用就会发现,有一定的局限性,就是前面说的各种类型用模板好用,是因为模板函数的实现代码是完全一样的,全是这样:
T = temp;
temp = a;
a = b;
b = temp;
无论short ,double,int等等,都是这样,所以模板好用。
但是假如来了个数组类型的a和b呢?首先函数实现代码不可能再这样写了, 但是无论是数组还是int,总的逻辑还是一个交换,只是参数类型不同了,我还想写成一个函数名swap。此时就需要用到:
显式具体化:
template<typename T>
void swap(T& a, T& b);
template <> void swap(int a[], int b[]);//显式具体化
int main{...}
template<typename T>
void swap(T& a, T& b)
{...}
template <> void swap(int a[], int b[])
{...}//后面要有自己的模板函数定义,定义好此种特殊类型的运算规则就可以了,这样就互不影响了。
显式实例化:
显式实例化是与隐式实例化相对的。
先说下函数模板的性质,
函数模板本身并不会生成函数定义,它只是一个生成函数定义的方案,告诉你怎么生成函数定义!!!程序想用函数,必须有函数定义,所以编译器就根据模板去生成函数定义,这个过程叫模板实例化。
一般情况下在什么时候实例化呢?就在程序中调用函数的时候隐式的去实例化:
swap(a, b);//这句就隐式的进行了模板的实例化
如果我不希望在函数调用时被隐式的实例化,而是想自己手动可控的去实例化函数模板呢,这就是显示实例化了。看程序:
template<typename T>
void swap(T& a, T& b);
template void swap<int>(int a, int b);//显式实例化,手动去将模板实例化为一个int类型的函数定义。
int main{...}
template<typename T>
void swap(T& a, T& b)
{...} //下方还是模板函数的定义,并没有实例化自己的定义
总结几个点:
1、显式具体化是告诉编译器,模板定义的运算规则对于这个类型不好用哈,我重新定义一个此类型的运算规则(一般像类与基本类型的一些同样名称的操作,在具体实现上,代码一般很不一样)。
显示实例化是我手动实例化出一个模板适用类型的函数定义,不想等着调用时隐式实例化出函数定义,比如上面手动实例化出的int类型交换函数定义。
2、显式具体化,用template <>
打头。显示实例化用template
打头。
3、显式具体化后方必须有自己的模板函数定义。显示实例化只需在模板原型下写一句实例化语句即可,不需要有自己的模板函数定义。
好,终于到了这里,看起来模板比较好用了,没有什么BUG了。但是,用着用着又发现问题了。。。
来看个膈应事:
template<typename T1, typename T2>
void sum(T1 x, T2 y)
{
?type? sum = x + y;//问题在这里,写到这一句的时候,这个sum定义成什么类型呢?
decltype(x + y) sum = x + y;//这样用就好了,表面上看其实也是一种后确定类型的方法,就是在x+y运算进行之前,我并不知道sum的类型,所以干脆就用这俩的运算来定义类型吧。
}
好了,decltype关键字来了,功能就是根据运算规则和运算元素自动去推断类型!
刚刚是赋值导致的事先不能确定类型的情况,还有就是函数返回值时:
template<typename T1, typename T2>
?type? sum(T1 x, T2 y)//模板的返回值类型怎么写呢?
{
return x + y;//模板有返回值了
}
模板的返回值类型你可能很自然的想到decltype(x + y),写成这样:
template<typename T1, typename T2>
decltype(x + y) sum(T1 x, T2 y)//直接把decltype(x + y)摆上,是不对滴!
{
return x + y;//模板有返回值了
}
图样图森跑,执行decltype(x + y)时,xy还没定义呢?哪来的xy??最早是在参数列表里定义的,你在参数列表之前用,不好使的。
所以很简单,移到后面不就可以了么。。。
template<typename T1, typename T2>
auto sum(T1 x, T2 y) -> decltype(x + y)//前面加个auto,后面来个->一指,完事。看起来有点像swift哦。。。
{
return x + y;//模板有返回值了
}
上一篇: c语言STL标准模板库(map)
下一篇: stl算法