1.内联函数
正常调用函数的时候,指针跳到函数所在的位置,执行完之后返回到调用该函数的内存处。内联函数的编译代码与其他程序内联起来,执行的时候无需跳到另一个位置处,但是会牺牲内存。
如果代码执行时间短,则内联调用就可以省去非内联调用使用的大部分时间;如果函数经常被调用,也可以使用内联;内联函数不能递归。
使用:在函数声明前加上关键字inline或者在函数定义前加上inline。
c语言中的宏定义是内联函数的原型,比如#define SQUARE(X) X*X,但是只是将X替换,不能按值传递。
2.引用
(1)引用是指针的伪装表示,但是其只能在初始化时赋值,类似于const类型的指针。
int & a=b; 等同于 int * const c=&b;
a和*c等同于b,&a和c等同于&b。a成为了b的别名。
(2)将引用作为函数参数,允许函数修改参数的值。
- 在函数中使用const类型的引用可以提高效率,使用原来的参数,不需要复制参数,并且函数不会改变参数的值。
- 仅当参数为const类型的引用时,如果实参与引用参数不匹配,c++将生成正确的临时变量。如果不是const类型,则实参与引用的参数类型不匹配时,将生成临时变量,引用修改的也只是临时变量,并没有修改原变量,所以此时的引用就会有警告。而const类型的引用本来就不改变原参数的值,因此这里的引用就是正确的。
如果引用是const类型,那么在以下两种情况下会生成临时变量:
实参类型正确,但不是左值;
实参类型不正确,但可以转换为正确的类型。
建议在不修改实参的情况下将引用参数声明为常量数据的引用,原因有以下几点:
const使函数能够处理const和非const的实参,否则将只能接受非const数据;
使用const引用能够使函数能够正确生成并使用临时变量。
(3)将引用用于结构
struct sysop{
char name[26];
char quote[64];
int used;
};
const sysop & use(sysop & sysopref)
{
sysop.used++;
return sysopref;
}
void main()
{
sysop looper={“sdfs”,"sdfsdf",4};
use(looper);
}
- 将结构looper按引用传递给函数,使得sysopref成为looper的别名;
- 使用引用作为函数的返回值,意味着调用程序将直接访问返回值,而不需要拷贝。(返回值不是引用的函数将返回值存储在一个临时的内存中,随后调用程序访问该临时内存)通常返回的是传递给该函数的引用,应避免返回当函数终止时不再在内存中存在的引用。
- 返回类型为const的引用只是意味着不能使用返回的引用来直接修改它指向的结构。
(4)将引用用于类
string version(const string &s1,const string &s2)
{
string tmp;
tmp=s2+s1+s2;
return tmp;} //正确,s1和s2都不会被改变.也可以使用char*型的变量作为实参
const sting & version2(string &s1,const string &s2)
{
s1=s2+s1+s2;
return s1;
} //有负面影响。这里s1就只能是string型的变量,char*的变量会出错。而s2就能够是char*型的变量。
const string & version3(string &s1,const string&s2)
{
string tmp;
tmp=s1+s2;
return tmp;
} //错误的,变量tmp在函数调用结束之后就会释放,他的值会存在一个临时内存中,不能将他作为引用来返回
(5)对象、继承和引用
基类引用可以指向派生类对象,不用进行强制转换。
(6)小结
- 何时使用引用参数:
修改调用函数中的数据对象;传递引用而不是整个数据对象。
- 指导原则:
不需要修改值时:
数据很小时,值传递;
数据对象是数组时,使用const指针;
数据对象时较大的结构,使用const指针或者const引用;
数据对象时类对象时,使用const引用。
要修改值时:
数据对象为内置数据类型,则使用指针;
数据对象为数组,则使用指针;
数据对象为结构,使用指针或引用;
数据对象为类,则使用引用。
3.默认参数
将值付给函数原型中的参数。
从右向左添加默认值,即要为一个变量赋默认值,必须使得它右边的变量都赋有默认值。
int function(int m,int n=10,int j); //错误
函数调用的时候从左向右将实参赋值给形参,不能跳过任何参数。
b=function(3,,5) //错误
4.函数重载
参数类型和任何重载函数的形参类型都不同时,c++将尝试强制转换,但是如果有多种转换方式都满足,则报错;
编译器在检查特征标的时候,将引用和类型本身视为同一特征标;
函数返回类型可以不同,但特征标必须不同。
5.函数模板
template<class Any> //这里写成template<typename Any>也可以,Any只是一个名称
void Swap(Any &a,Any &b)
{
Any tmp;
tmp=a;
a=b;
b=tmp;
}
template<typename Any> //这个在函数声明和函数定义的前面都要加上
void Swap(Any [],Any [],int) //模板的重载应用。模板函数的形参并不一定都是模板参数
{
Any tmp;
for(int i=0;i<n;i++)
{
tmp=a[i];
a[i]=b[i];
b[i]=a[i];
}
}
- 对于给定的函数名,可以有非模板函数、模板函数和显示具体化模板函数以及它们的重载版本。
- 显示具体化的原型和定义应以template<>打头,并通过名称来指出类型。(为什么不用非模板函数来替换显示具体化模板函数)
- 编译器在选择原型时,非模板版本将优先于显示具体化和模板版本,显示具体化将优先于模板版本。
struct job;
void Swap(job &a,job &b);
template<typename Any>
void Swap(job &a,job &b);
template<>void Swap<job>(job &a, job &b); //或者也可以写成template<>void Swap(job &a,job &b).