C++ Primer Plus笔记05

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。

将引用作为函数参数

fimes是times,图中为打印错误
按照图中所示,按值传递,会创建一个临时变量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对象作为参数。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值