C++ Primer Plus笔记05
内联函数
为了提高函数运行速度,对于那些经常调用的,而且比较小的函数,如果经常跳到他们的指令地址上去处理就会很浪费时间。
典型实现方法:
执行到函数调用指令时,程序将在函数调用后立即储存该指令的内存地址,并将函数参数复制到堆栈(为此保留内存块),跳到标记函数起点的内存单元,执行函数代码(也许还需将返回值放入到寄存器中),然后跳回到地址被保存的指令处。来回跳跃会有时间开销。
而内联函数:
编译器将使用相应的函数代码替换函数调用。对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。代价是要付出更多内存。
如何定义声明内联函数
关键字:inline
例:
inline double square(double x) {return x*x;}
1.按值传递参数。(三种传递方式:值传递(副本),引用传递(原始数据),地址传递(原始数据))
2.b = square(4.5+7.5);
与b = square(12);
结果一样,这与C的宏定义有所不同。
3.给函数传递int或long,函数会强制转换为double类型。
引用变量
引用就是已定义的变量的别名。主要用途在于作函数的形参。通过将引用变量做参数,函数使用原始数据,而不是副本。
创建引用变量
使用&符号:
int rats;
int & rodents = rats;
&在声明的时候不是取地址符,而是引用。就如同int * p=&a;
一样,*在里面表示p是一个指针变量。
而&在int & rodents = rats;
中表示rodents是一个引用变量。
声明之后,rodents和rats就指向相同的值和内存单元。
和指针的差别
必须在声明引用时将其初始化,而不能像指针那样,先声明,在赋值。
int rats;
int & rodents;
rodents = rat; //No, you can't do this.
如果rodents = bunnies;
则rats和rodents的值都会一起改变。
若有下例:
int rats = 101;
int * pt = &rats;
int & rodents = *pt;
int bunnies = 50;
pt = &bunnies;
将rodents初始化为*pt使得rodents指向rats。接下来将pt改指向bunnies,并不能改变rodents引用的是rats。
将引用作为函数参数
按照图中所示,按值传递,会创建一个临时变量x,运用这个副本进行操作。
引用传递则会使x作为times的引用,相当于直接使用times原始数据,不会创建临时变量。
当数据比较大的时候(如结构和类)时,引用参数将很有用。
区别
如果用按值传递,以下形式的实参都是合法的:
x+20、8.0、k、yo[3]、
如果用引用传递,以下形式的实参是不合法的:
x+3.0:因为不能值赋给表达式x + 3.0 = 5.0;
不成立
临时变量,引用参数和const
如果实参与引用参数不匹配,C++将生成临时变量。仅当参数为const引用时,C++才允许这么做。
以下两种情况:
1.实参类型正确,但不是左值
2.实参类型不正确,但可以转换为正确的类型
左值
变量、数组元素、结构成员、引用和解除引用的指针都是左值
字面常量(用引号括起来的字符串除外,他们由地址表示)和包含多项的表达式都是非左值
常规变量属于可修改的左值,而const属于不可修改的左值。
判断
double refcube(const double & x){return x*x*x;}
double side = 3.0;
double * pd = &side;
double & rd = side;
long edge = 5L;
double lens[4] = {2.0, 5.0, 10.0, 12.0 };
double c1 = refcube(side);
double c2 = refcube(lens[2]);
double c3 = refcube(rd);
double c4 = refcube(*pd);
double c5 = refcube(edge);
double c6 = refcube(7.0);
double c7 = refcube(side + 10.0);
side、lens[2]、rd、*pd都是有名称、double类型的对象,可以为其创建引用,而不需要临时变量。
然而edge类型不正确,double引用不能指向long,side+10.0类型正确但没有名称,这样编译器会产生一个临时匿名变量(副本),并让ra指向它。临时变量只在函数调用时存在,调用结束后编译器可以将其任意删除。
如果函数调用的参数不是左值或与相应的const引用参数的类型不匹配,则C++将创建类型正确的匿名变量,将函数调用的参数的值传递给该匿名变量,并让参数来引用该变量。
尽可能使用const
原因:
1.使用const可以避免无意中修改数据的编程错误;
2.使用const使函数能处理const和非const实参,否则将只能接受非const实参
3.使用const引用使函数能够正确生成并使用临时变量
右值引用
使用&&来声明
double && rref = std::sqrt(36.00); //现在看不出来有什么用
结构作为函数引用参数
struct free_throws
{
std::string name;
int made;
int attempts;
float percent;
};
void set_pc(free_throws & ft);
void set_pc(const free_throws & ft); //若不想改变结构的数据
有这样的一个函数free_throws & accumulate(free_throws & target,const free_throws & source);
第一个参数是结构的引用,第二个参数是const引用
但看下面的调用:
display(accumulate(team, two)); //team和two都是free_throws类型的结构
accumulate(team, two)
这个函数return target; 所以返回了一个引用。
但如果函数原型是:free_throws accumulate(free_throws & target,const free_throws & source);
则会返回target的拷贝(副本)。
接下来display函数的参数是一个结构的引用,意味着,accumulate的返回的对象传递给了display的参数作为引用。
最后讲一下这个语句:
accumulate(dup,five)=four; //dup,four,five都是free_throws类型的结构
为什么可以这么用呢?因为accumulate返回的是引用,可以直接将four的数据通过赋值符号,直接赋值给引用对象。
为何要返回引用
按值传递时,会将返回值复制到一个临时变量里,然后再赋值给相应的变量。也就是当返回结构时,要复制一份副本到临时变量,再赋值给相应结构。
而引用传递时,可以直接赋值,无需创建临时变量,效率更高。
需要注意的问题
一定要避免返回函数终止时不在存在的内存单元的引用。也就是如果在函数内部定义了一个变量或者结构,返回值不能是对他的引用。因为函数结束时,局部变量的内存单元会被清空。
const free_throws & clone2(free_throws & ft)
{
free_throws newguy;
newguy = ft;
return newguy; //这样是不行的,函数终止,newguy的数据会被清空,返回了一个不知地址,不知值的内存单元
}
将引用用于类对象
如
string version1(const string &s1, const string & s2)
{
string temp;
temp= s2 + s1 + s2;
return temp;
}
如果用string version1(const string s1, const string s2)
结果也不变。
但如果用引用就不会创造副本,直接运用原数据进行改动,效率会提高。const指出原实参不会被改变。
temp是在函数中被创造的,函数调用结束就会消失,所以不能返回他的引用。
将C-风格字符串作string对象引用参数
程序能将char指针付给string引用吗?
string类定义了一种从char* 到string的转化功能,所以可以用C-风格字符串初始化string类对象。
假设实参的类型与引用参数类型不匹配,党课被转化为引用类型,程序将创建一个正确类型的临时变量,使用转化后的实参值来初始化他,然后传递一个指向该临时变量的引用。如将int实参传递给const double&形参。
所以形参类型为const string &时,实参类型可以是string对象或C-风格字符串,如引号括起的字符串字面值、以空字符结尾的char数组或指向char的指针变量。
文件的输入/输出
ostream是基类,而ofstream是派生类(因为ofstream是从ostream派生而来)。派生类继承了基类的方法。
继承的另一个特征:基类可以指向派生类对象,无需强制类型转换。所以可以定义一个接受基类引用作为参数的函数,调用该参数时,可以将基类对象作为参数,也可以将派生类作为形参。
如,参数类型为ostream &的函数可以接受ostream对象(如cout)或已声明的oftream对象作为参数。