【c++ primer】第8章读书笔记

来源:互联网 发布:mac显示桌面快捷方式 编辑:程序博客网 时间:2024/05/19 06:15

//单纯写给自己看的读书笔记,只是觉得可能有人也在和我看同样的书,就拿出来和大家一起分享一下此刻的思想,不喜勿喷。 要批评请具体到我哪点说错了。



第八章函数探幽

    1.内联函数

    一个程序在运行时,对于常规函数,当程序主函数运行到函数调用部分的时候,会根据函数名的地址,将程序运行跳转到函数体所在内存位置上,在函数体内存位置上运行函数,当函数运行结束返回值时,程序在从函数体内存位置跳转回原主函数中继续下一行的运行。

    对于内联函数,编译器自动把内联函数的函数体复制到程序主函数调用内联函数的位置上去,因此在运行时,省去了程序跳转到函数地址的时间,同时省去了从函数地址处运行完函数跳转回原程序的时间。

在常规函数运行时,函数体所在内存位置和空间是固定的,即使被调用N次,它也不会增加内存的占用。而内联函数不一样,每一次的调用相当于复制了原函数一次,因此它占用的内存就会变多。比如,在主函数中调用了一次内联函数,那么在内存中,就有两个地方的在编译后的内存情况是一样的,一个是定义函数的地方,另外一个是主函数中调用内联函数的地方,程序只是把你写的定义函数的代码再复制到你所调用函数的地方。

因此可以总结内联函数的使用情况。当函数体内存占用较小且再程序中调用次数较少的情况下使用内联函数可以提高计算效率。

inline typeName function_name(typeName parameter);//函数申明inline typeName function_name(typeName parameter){    statement };//函数定义


2.引用变量

    创建引用变量的形式

int rats;int & rodents=rats;
     注意:引用变量只能再定义时指定其值,之后就就和普通变量名一样。

     什么是引用变量,其实就是一个变量的别名。以上面的形式为例,rodents是rats的引用变量,即这两个变量名拥有相同的地址,指向的都是同一个地址的内存空间。

     那么我们开始关注这个引用变量实际的用法了,在定义函数时,由于函数内部定义的变量都会在函数结束时消亡,因此当需要保存运行结果的时候,需要函数外部定义的变量加入,或者在函数体内使用指针并用New来向内存申请空间,变量值需要被复制到这个内存空间上才能被保留。再者,如果使用传统的方法定义形参为常规变量,这样调用时函数参数变量会复制调用进函数的参数,即将int x=a;这样复制一次内存增加了内存占用,同时减慢了运行速度。


e.g. 交换变量中的用途

int wallet1=300;int wallet2=350;
swapr(wallet1,wallet2);//两值交换成功
swapp(&wallet1,&wallet2);//两值交换成功
swapv(wallet1,wallet2);//失败
swapr(int &a,int&b)
{
int temp;
temp=a;
a=b;
b=temp;
}
void swapp(int *p,int *q)
{
int temp;
temp=*p;
*p=*q;
*q=temp;
}
void swapv(int a,int b)
{
int temp;
temp=a;
a=b;
b=temp;
}

运行上述程序发现,第三种方法交换两者失败了,原因形参a,b是复制了wallet1,wallet2的值,然后调用函数体进行计算的,所以它并没有影响到原来参数的值。


另外,在把形参定义为引用参数时,可以使用CONST关键词,与之前形参使用CONST的效果一样,它使得我们的引用参数的值不受修改。这好像与上面理解的引用变量的使用方法有出入,确实引用变量在修改原始数据方面效果很好,但是当我们使用引用变量的目的不是修改原始数据,而只是希望在调用函数时,参数不进行复制(即目的是为了节省内存时),使用CONST关键词和之前的常规参数使用CONST效果是一样的,或者说效果得到了提升,原因是:当变量类型不正确的时候我们依然能够调用函数而编译器只是发出警告。实际上,对于形参为CONST引用的C++函数,如果实参不匹配,则其行为类似于按值传递,为确保原始数据不被修改,将自己生成适合载入实参的临时变量来存储值。

所以除非是需要修改变量的值,我们因尽可能的使用CONST。

double refcube(const double &ra){    return ra*ra*ra;}
可以把引用用在函数头上

const free_throws & clone(free_throws & ft)

这样函数的返回值就是一个引用,原理和引用参数一样。当我们使用引用函数头时,函数的返回值将不进行一次临时的复制,而是直接把计算完后,结果的地址的位置上的值赋值给其他变量。注意,不能把函数体内定义的变量作为最后的输出地址,因为在程序结束运行的那时,这些变量就已经消失了。


3.默认参数

默认参数的使用:在函数原型中声明:

int harpo(int n,int m=4,int j=3)//合法int chich(int n,int m=3,int j)//不合法int dchss(int n=3,int m=4,int j=3)//合法
主要指出,对于带参数列表的函数,必须从右向左添加默认值。也就是说,要为某个参数设置默认值,必须为它右边的所有参数提供默认值。

同时,实参按从左到右的顺序被付给形参,而不能跳过任何参数。下面的调用时错误的:

beeps=harpo(3, ,8)//不合法


4.函数重载

函数重载的关键是函数的参数列表———也称为函数特征标(function signature)。如果两个函数的参数数目和类型相同,同时参数的排列顺序也相同,则它们的特征标相同,而变量名是无关紧要的。C++允许定义名称相同的函数,条件是它们的特征标不同。如果参数数目和/或参数类型不同,则特征标也不同。

这里要注意的是引用参数和常规参数在函数重载里面是相同的,编译器都把引用参数和常规参数看成常规参数。

虽然重载函数很吸引人,但也不要滥用。仅当函数基本上执行相同的任务,但使用不同形式的数据时,才应采用函数重载。


5.函数模板

用法:

声明部分:

template <typename AnyType>void Swap(AnyType &a,AnyType &b)

定义部分:
template <typename AnyType>void Swap(AnyType &a,AnyType &b)
{
statement;
}

这里指出,typename可以被class代替。函数类型void也可以模板化。重载操作也可以用于函数模板。但是具体函数模板在运用时,有些类型会受到限制,所以在编写函数模板时,要确保你会用到的函数类型在这个模板上的运算都是正确的。


显示具体化:理解为当你想对模板接受的参数类型中的特定的某个参数类型执行不同于模板函数的操作。需要显示具体化函数定义。

template<typename T>void Swap(T & , T &);//模板定义template<> void Swap<job>(job & , job &);//显示具体化


ps:编译器调用函数的顺序,非模板函数 > 具体化函数定义 > 常规模板函数

当编译器找到适合参数的函数时,便不在寻找其他函数了。

隐式实例化:最普通的调用函数模板的方法。

显示实例化:与显示具体化不同的是,显示实例化是强制定义T/AnyType的类型,然后生成与模板相同的函数,而显示具体化是自己定义新的函数。

我们可以通过显示实例化强制定义函数类型,来完成一些参数类型的强制转化,例如 int 转变为 double (有些情况下是错误的)。


编译器匹配模板的顺序

1.完全匹配,但常规函数优先于模板

2.提升转换(例如,char和shorts自动转换为int,float自动转换为double)

3.标准转换(例如,int转换为char ,long转换为double)

4.用户定义的转换,如类声明中定义的转换


0 0