①传值:
优点:对函数形参改变不影响外部实参,对外部实参起到一种保护作用。
缺点:不能通过形参改变外部实参,浪费空间,降低代码的运行效率。
②传地址:
优点:可以通过形参改变外部实参,提高传参效率。
缺点:函数的副作用影响外部实参,可能存在空指针的情况,降低代码的安全性,另一方面不太便于理解。
概念:引用是变量的一种别名,不会开辟另外的空间,与实体公用同一块内存空间。
特性:
a.定义期间必须初始化(有实体)
b.引用和被引用的类型相同。
c.一个实体可以有多个别名。(存在多个引用)
d.一个引用一旦被用于引用一个实体不能被用于其他实体。
e.引用的生命周期一定比实体短
const类型引用主要作用是:①不想通过形参改变外部实参,因为引用是变量的别名,引用被改变防止给外部变量所带来的副作用通常在引用的类型名前加const.提高安全性。
void swap(const int&left,const int&right)
{
int temp=left;
left=right;
right=temp;
}
int main(){
int a=10;
int b=20;
swap(a,b);
}
②在对变量进行引用时,引用类型跟变量类型不同,可在引用前加const.起到开辟临时变量的效果
double d=12.34;
const int& rd=d;//rd和d的地址并不相同,rd是临时开辟出来的临时变量用于保存12.34的整形部分,具有常性。
①引用作为变量的别名
②引用作为函数返回值类型
int &Add(int left,int right)
{
int ret=left+right;
return ret;
}
int main()
{ int&r=Add(1,2);
Add(3,4);
return 0;}
注意a.如果按照引用方式返回,一定不能返回栈上空间。
b.返回变量的生命周期不受函数结束的影响。(类似还有:全局变量,static修饰的局部变量,函数的参数列表的引用)
传值:
函数参数压栈的是参数的副本。
任何的修改是在副本上作用,没有作用在原来的变量上。
传指针:
压栈的是指针变量的副本。
当你对指针解指针操作时,其值是指向原来的那个变量,所以对原来变量操作。
传引用:
压栈的是引用的副本。由于引用是指向某个变量的,对引用的操作其实就是对他指向的变量的操作。
底层实现方式上:引用在底层就是按指针方式实现。T& <=> Tconst ,const T&<=> const Tconst。
应用层面上:
指针 | 引用 |
---|---|
指针在使用时选择性赋空 | 引用在使用时必须初始化 |
指针不是const类型可随意指向 | 引用一旦被一个实体引用后不能被其他实体引用 |
指针可以指向NULL | 引用不能为NULL而且必须初始化 |
"sizeof引用"得到的是所指向的变量(对象)的大小 | "sizeof指针"得到的是指针本身的大小 |
++(引用)是实体值加1 | ++(指针)连续空间,偏移相同类型的单位 |
指针可以有多级(int **p;合法) | 但是引用只能是一级(int &&a是不合法的) |
指针是显示解引用 | 引用是编译器自动解引用 |
指针要判空 | 引用更安全 |
内联函数在编译时在内联函数调用位置使用内联函数展开,内联函数在普通函数前加‘inline’.
特性:a.在’release‘下才有可能展开,’debag’条件下不能展开
b.内联函数是建议性的操作,遇到复杂代码(递归,循环)编译器会自动忽略。
c.定义和声明不建议分开,内联函数只能在当前文件中使用,当展开后地址不会被保存,可能因为找不到地址而报错。
优点:内联函数在编译时展开,省略了变量进栈出栈所消耗的时间,是一种以空间换时间的操作,省去了调用开销,但是如果代码复杂会自动被忽略。
(1) 在一个文件中定义的内联函数不能在另一个文件中使用。它们通常放在头文件中共享。
(2) 内联函数应该简洁,只有几个语句,如果语句较多,不适合于定义为内联函数。
(3) 内联函数体中,不能有循环语句、if语句或switch语句,否则,函数定义时即使有inline关键字,编译器也会把该函数作为非内联函数处理。
(4) 内联函数要在函数被调用之前声明。关键字inline 必须与函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何作用。
内联函数 | 宏函数 |
---|---|
编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数) | 而宏定义没有类型检查 |
内联函数在运行时可调试(release下可展开) | 宏定义不可以 |
内联函数可以访问类的成员变量 | 宏定义则不能 |
进行代码展开,内联函数则是在编译阶段把所有调用内联函数的地方把内联函数插入 | 进行代码展开,宏定义是在预编译的时候把所有的宏名替换 |
内联函数和普通函数相比可以加快程序运行的速度,因为不需要中断调用,在编译的时候内联函数可以直接被镶嵌到目标代码中。内联函数要做参数类型检查,这是内联函数跟宏相比的优势。inline是指嵌入代码,就是在调用函数的地方不是跳转,而是把代码直接写到那里去。对于短小的代码来说,inline可以带来一定的效率提升,而且和C时代的宏函数相比,inline 更安全可靠。可是这个是以增加空间消耗为代价的。至于是否需要inline函数就需要根据你的实际情况取舍了。
C中:修饰局部变量,变量出局部变量时销毁(同一般函数,所以没有用)
C++:可以不使用类型定义变量,但是‘auto’不是类型的声明,所以必须初始化,相当于占位符,根据变量的实际类型进行推演
特性:a.在定义指针时是否加’ * '所产生的效果相同,( auto pa=a <=> auto*pa=a)
在定义引用时必须加 ‘ & ‘否则意义不同。 (auto ra=a != auto&a=a)前者是与a同类型的普通变量,后者是a的引用。
b.auto可以定义多个变量,但前提是变量的类型相同。
c.不能作为函数的参数类型,例如:void TestAuto(auto a=10) 编译器不能对a进行实际类型推导,认为函数不完整
d.不能定义数组
对于确定的范围进行循环
void mul(int *array)
{
for(auto&e:array) //对每个array的值改变,所以传引用
e=e*2;
for(auto e:array)
cout<
nullptr是c++11中的关键字,表示空指针要区分nullptr和NULL,首先要明白NULL的含义:NULL是一个宏定义,在c和c++中的定义不同,c中NULL为(void*)0,而c++中NULL为整数0
//C语言中NULL定义
#define NULL (void*)0 //c语言中NULL为void类型的指针,但允许将NULL定义为0
//c++中NULL的定义
#ifndef NULL
#ifdef _cpluscplus //用于判定是c++类型还是c类型,详情看上一篇blog
#define NULL 0 //c++中将NULL定义为整数0
#else
#define NULL ((void*)0) //c语言中NULL为void类型的指针
#endif
#endif
所以在c++中int *p=NULL; 实际表示将指针P的值赋为0,
而c++中当一个指针的值为0时,认为指针为空指针然后理解nullptr:nullptr是一个字面值常量,类型为std::nullptr_t,空指针常数可以转换为任意类型的指针类型。
在c++中(void *)不能转化为任意类型的指针,即 int p=(void)是错误的,但int *p=nullptr是正确的,原因对于函数重载:若c++中 (void *)支持任意类型转换,函数重载时将出现问题下列代码中fun(NULL)将不能判断调用哪个函数
void fun(int i){cout<<"1";};
void fun(char *p){cout<<"2";};
int main()
{
fun(NULL); //输出1,c++中NULL为整数0
fun(nullptr);//输出2,nullptr 为空指针常量。是指针类型
}