目录
一.引用
1.引用的定义
2.引用的特性
3.引用的使用场景
4.const引用
5.引用和指针的区别
二.inline
三.nullptr
一.引用
1.引用的定义
引用不是新定义一个变量,而是给已经存在的变量取一个别名,编译器不会给引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。简单来说,引用就是起别名,例如水浒传中林冲又名豹子头,这就是取别名,同时,林冲死了也就意味着豹子头死了,两者指向的都是同一个人。
用法:类型&引用别名 = 应用对象;
int main()
{
//对a起别名b和c
int a = 0;
int& b = a;
int& c = a;
cout << a << endl;
cout << b << endl;
cout << c << endl;
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
return 0;
}
2.引用的特性
引用在定义的时候必须初始化。因为引用是一个别名,它的存在建立在主体存在的基础上,也就是说必须引用一个已经存在的对象,否则会引起编译报错。
一个变量可以有多个引用。正如一个人可以有多个外号,但这些引用都指向同一个对象。
引用一旦引用另一个实体,就不能再引用其他实体。引用一旦建立,就不能更改为其他实体的引用,这种“偷外号”的行为是不允许发生的。
允许引用其他引用。例如:int& b = a,int &c = b;此时就是c引用了b,但b也是引用的a,此时,b,c都是a的别名。
int a = 0;
int& b = a;
int c = 20;
//b已经引用a的情况下,不能更改为引用c
//以下直接看作赋值
b = c;
cout << a << endl;
cout << b << endl;
cout << c << endl;
3.引用的使用场景
引用在实践中主要是用于引用传参和做返回值,能够减少拷贝提高效率,同时能够改变引用对象和被引用对象。
引用传参跟指针传参的功能是类似的,但引用传参看着更简洁也更方便。例如,对于Swap函数的实现而言,引用传参可以这样实现:
void Swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
引用返回值就要看使用场景了,例如在栈(Stack)的实现中,有一个函数叫STTop用于得到栈顶元素,返回值是int,只是一个数值的拷贝,具有常性,无法做左值,即无法进行修改。
但若返回值改为int&,那么此时就是对返回的栈顶元素取别名,那么就可以进行修改了。如下所示
typedef int STDatatype;
typedef struct Stack
{
int* a;
int capacity;
int top;
}ST;
//直接引用传参
void STInit(ST& rs, int n = 4)
{
rs.a = (STDatatype*)malloc(sizeof(STDatatype) * n);
rs.capacity = n;
rs.top = 0;
}
void STPush(ST& rs, STDatatype x)
{
rs.a[rs.top] = x;
rs.top++;
}
//int STTop(ST& rs)
//{
// return rs.a[rs.top - 1];
//}
int& STTop(ST& rs)
{
return rs.a[rs.top - 1];
}
int main()
{
ST s;
STInit(s,4);
STPush(s, 1);
STPush(s, 2);
STTop(s) = 4;
return 0;
}
4.const引用
const引用可以用来引用普通对象,普通对象是能读能写,但const引用后,引用是只读不写,注意:对象本体还是可以写的,只是引用(别名)用const修饰了不能写,这叫权限缩小。
const引用也可以用来引用const对象,此时就叫权限平移,因为引用和本体的权限是相同的,都被const修饰,只能读不能写。
普通引用不能用来引用const对象,这是权限放大。原本本体就不能被修改,但引用后反而能被修改了,这合理吗?这种写法是禁止的,也会进行如下报错。
引用临时对象需要使用const引用。什么是临时对象?临时对象出现在以下这几种情况:
类型转化:如下double类型转化为int引用
double a = 1.1;
const int& b = a;
表达式结果:如下a*3就是一个表达式,需要临时对象储存结果
int a = 1;
const int& b = a*3;
5.引用和指针的区别
- 引用必须初始化,指针建议初始化,但语法上不是必须的
- 引用不开辟空间,但指针是储存一个变量地址,需要开空间
- 初始化后引用不可修改指向对象,而指针可以修改指向对象
- 引用直接访问指向对象,指针需要解引用才能访问
- sizeof时,引用是引用对象类型的大小,而指针始终是地址空间所占的字节个数(32位下是4字节,64位下是8字节)
- 引用更加安全,指针容易出现空指针和野指针的问题
二.inline
使用inline修饰的函数就是内联函数,编译时编译器会将其在调用的地方展开,这样调用内联函数就需要建立栈帧了,可以提高效率。inline就是为了替代C语言中宏函数而实现的,宏函数实现很复杂而且容易出错,不方便调试,inline才运营而生。
当然inline对于编译器而言只是一个建议,是否展开取决于编译器本身,一般来说,inline适用于频繁调用的短小函数,对于递归函数,代码较多的函数,就算加上inline编译器也会选择忽略。
line不建议声明和定义分离到两个文件,分离会导致链接错误。但可以在同一个项目的不同源文件内定义函数名相同但实现不同的inline函数。
注:class类中的函数默认是inline
三.nullptr
在C语言中,NULL其实是一个宏,它的定义是:
#ifndef NULL
#ifdef __cplusplus
#define NULL
#else
#define NULL ((void *)0)
#endif
#endif
那么在使用时就会产生歧义,例如在调用空值的指针时,就有可能调用到int类型的重载函数,例如:
void f(int x)
{
cout << "f(int x)" << endl;
}
void f(int* x)
{
cout << "f(int* x)" << endl;
}
int main()
{
f(0);
f(NULL);
return 0;
}
//结果是两次都是调用的f(int x)
因此在C++11中引入了nullptr,这是一个特殊的关键字,它可以转化为任意类型的其他指针类型,但不能转化为整数类型,这就避免上图的出错。
看到这里,就能对有了一定的C++基础知识,但还不算入门,接下里我会持续更新类和对象的内容,那才是面向对象编程的精髓。感谢观看,欢迎关注。