1.C++的内联函数和普通函数区别
- C++中普通函数的工作原理:
当程序运行到调用函数的代码时,首先程序会在函数调用后的第一时间存储该指令的内存地址,然后通过程序告知的函数所在的内存地址,并且将函数的参数复制到堆栈中,随后跳转到函数起点所在的内存单元,执行完程序之后,如果由返回值,会将返回值放到指定的内存单元或cpu寄存器中,然后跳回到原来保存的内存单元,继续向后执行程序。跳转的过程由一定的开销 - C++内联函数的工作原理:
内联函数不再像普通函数那样,需要跳转到函数存放的单元,内联函数的编译代码和其他程序代码”内联“起来了。也就是编译器会使用对应的函数代码来代替函数调用,也就是说程序无需再通过记录内存位置,跳转,再跳转的方式来执行函数。而是直接执行代码即可。这样的执行方式会节省跳转以及找寻内存单元的时间。不过也可能导致内存占用空间变大,因为内联函数是使用的是函数代码,也就是说,无论程序有多少处使用此函数的地方,就一定有多少个函数副本在内存中。所以使用的时候要注意。
2.C++内联函数的使用方式
- 在函数声明前加关键字 inline
- 在函数定义前加关键字 inline
通常的做法是省略函数原型,将原本放函数原型的位置放函数的整个定义
不过内联函数还有几点需要注意的地方:
- 内联函数不能使用递归
- 内联函数不建议(不能)过大
#include<iostream>
using namespace std;
inline double square (double num) {
return num*num; }
int main()
{
double x = 5.0;
cout << square(x) << endl;
return 0;
}
3.引用变量的定义与创建
引用变量是一种复合类型的变量,引用是一定义的变量的别名。下面介绍创建方式:
//复习
// 地址运算符& 在之前是用来得到目标变量的地址而使用的
// 而今天,&将被赋予另一项权能,那就是声明引用
//创建引用变量
int i;
int &b = i;
//这里的&与*一样 代表类型说明符
//*代表声明对象是一个指针
//而&代表声明的对象是一个引用
引用变量和原变量指向相同的值和内存单元,所以,简而言之,引用变量就好像是一个别名,就好像你大名叫王二狗,小名叫铁柱一样,他们都代表了你这个人。
下面的程序也说明了这一点:
#include<iostream>
using namespace std;
int main()
{
int wangergou = 52;
int &tiezhu = wangergou; //从现在开始铁柱就是二狗的小名了
cout << wangergou << endl;
cout << tiezhu << endl;
tiezhu++;
cout << wangergou << endl;
cout << tiezhu << endl;
return 0;
}
注意:必须在声明引用的时候将他初始化。而不能在之后初始化。
引用更加接近const指针,必须在创建的时候进行初始化,而且一旦指向了一个变量,就不能再改变。(意外的忠贞)
int boy1 = 250;
int * son = &boy1; // ①
int &boy1_anotherName = *son ; // ②
int boy2 = 521;
son = &boy2; //③
//首先①中声明了一个指针son,指向boy1
//也就是*son和boy1拥有同样的数据,指向同样的地址
//换句话说 son 和 boy1是一个人
//然后语句②中声明了一个引用变量boy1_anotherName 指向了 *son
//目前的*son 代表的是 boy1 他们拥有相同的内存地址和值
//那么此时boy1_anotherName 也拥有了和boy1以及son相同的内存地址
//而且根据引用特性 这句话与 int* const boy1_anotherName = *son; 同义
//也就是说 boy1_anotherName 永远的和 代表boy1或者son的这块内存地址相连
//最后③中,son指针指向了boy2,也就是son舍弃了原来的内存地址
//son拥有了新的内存地址,和新的数值
//而boy1_anotherName依然代表的是原来的内存地址
//代表的是boy1和250
4.将引用用作函数参数
引用变量常用于函数的参数传递之中,将引用变量作为参数传递,使得函数名调用的是程序中变量的别名,这种传递方法是C++较C语言新增加的特性—按引用传递。
//编写一个按引用传递的函数
//函数原型
void swap (int & a, int & b);
//函数体
void swap (int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
}
- 为什么要使用按引用传递?
在C++的按值传递函数中,被调用函数实际上使用的是参数的副本,也就是无法对于原来参数进行修改,这样导致需要修改原参数的函数束手无策。当然可以通过指针的方式进行修改,所谓的指针传递也是一种按值传递,只不过传递的值变成了指针值,也就是地址。而传递过去的地址值,就可以通过函数找到所需修改的值的地址,然后对值进行修改了。而引用传递也可以如同指针那样,完成修改原参数的工作。
5.指针传递和引用传递的区别
- 声明函数参数的方式不同
- 使用指针传递的参数时,需要使用解除引用运算符
6.引用的属性和特别之处
举个小例子:
//伪代码
double x = 3.0;
cout << square (x) << x ;
double square (double &x)
{
return x *= x * x;
}
在这种情况下,输出的结果将会是27,27,也就是不光结果值会输出,而且x的值会随之改变,这也就是引用变量的特点。但是有时候我们并不希望这个值改变,最好的办法当然是将他生成为按值传递的函数,不过可能会有一些情况确实要使用到引用和变量,那么我们就要把他生成为const类型的引用变量。
//一个好办法
double square (double x)
{
return x *= x * x;
}
//仍然要使用引用变量
double square (const double &x)
{
return x *= x * x;
}
7.引用变量的参数
//在值传递中
square(x+2.0);
square(x*3);
//这样的语句都是允许的
//但是在引用传递中并不是如此
//因为引用传递的内容相当于是一个变量
square(x+2.0);
//这样的代码不合理也很好说明
//因为x+2.0并不是一个变量
//但是在早期的C++中这种语法是允许的(现在不允许)
在早期的C++中,遇到引用变量的(x+2.0)这种表示方法,系统判断出x+2.0并不是属于double类型的变量,那么系统会生成一个无名变量,其将其初始化为x+2.0表达式的结果。然后x将成为该临时变量的引用。下面我们来介绍一下什么时候会创建这种临时变量,什么时候不会创建。
8.临时变量,引用参数 和 const
如果实参和引用参数不匹配,C++会生成临时变量,目前,仅当const作为参数引用时,才会允许这么做。那么const作为引用参数时,什么情况会生成临时变量呢?
-
实参的类型正确,但不是左值
左值:左值参数是可被引用的数据对象,例如:变量,数组元素,结构成员,引用和解除引用的指针。【const和常规变量都可以看作左值,因为都能通过地址找到他们】
非左值:包括字面常量(用括号扩起的字符串除外,他们由地址表示)和包含多项的表达式。 -
实参的类型不正确,但是可转换为正确的类型
-
为什么C++会禁止非const类型作为引用参数时的临时变量呢?
因为当C++发现实参和引用参数不匹配的时候,C++会创建临时变量,而让引用参数指向新建成的临时变量,这导致引用参数和原本要使用的参数之间断开联系,也就是说,对于引用参数的修改不能传递会给我们的实参,这可能会导致要修改原参数的函数出现错误。所以C++后来禁止了这种情况下临时无名变量的产生。 -
那为什么仍保留着const类型作为引用参数时的临时变量?
因为const类型属于常量,他的值不允许被修改,所以就排除了需要修改实参这种情况,所以临时变量不会造成任何不好的影响,甚至还可以使能够传递的参数种类更加通用。
应该尽可能使用const
- 使用const可以避免无意修改数据的编程错误
- 使用const使函数能够处理const和非const实参,否则只能接受非const数据
- 使用const引用使函数能正确生成并使用临时变量
9.右值引用 &&
之前我们的引用方法成为左值引用,顾名思义,是用来对左值进行引用的,左值的概念我们以及清楚了,下面就举几个小例子来形象理解一下:
//左值引用
//左值包含 引用和解除引用的指针 变量 const常量 数组元素 结构成员
//也就是可以被引用的数据对象
//所有有名字的量都是左值
int i; //i是左值
int *p; //*p是左值
const int i; //i是左值
int i[20]; //i[0],i[1]...是左值
string str = "foo";//str也是左值
//对于左值可使用左值引用
int & b = i; //允许
int & c = *p; //允许
int & d = c; //允许
int & e = i[2]; //允许
//左值引用常用来向函数传递比较大的参数,或者是可以进行修改的参数
//右值引用
//右值包括含有多项的表达式,字面常量
int i = 2; //这里的2是右值
int i = x + y; //x+y也是右值
//右值不允许使用左值传递
int &a = 2; //不允许
int &a = x + y; //不允许
//C++11中加入了右值引用,用来对右值进行引用
int &&a = 2*x + y; //允许
//右值引用常用于移动语义