一.引用
1.1引用的概念和定义
#include <iostream>
using namespace std;
int main()
{
int a = 0;
//注意这里的b和c就是a的别名
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;
}
打印出来是这样的:
也就是说,引用其实就是给一个变量在起一个名字,并不会重新创建额外空间。
1.2引用的特性
因为引用是一个别名,它必须引用一个已经存在的对象。换句话说,引用必须指向某一个具体的对象。如果在定义引用时没有进行初始化,就会导致引用无法指向任何已存在的对象,这将引发编译错误。
int main()
{
int a = 0;
int& b = a;
int c = 20;
b = c;
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
return 0;
}
打印出来:
自然是不一样的,因为我刚开始就给a取了一个别名叫做b,如果再把c赋值给b,这里的赋值就仅仅只是把c的值给了b。因为b已经引用了一个实体a,不能再引用其他的实体。
1.3引用的使用
void Swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
//初始化
void STInit(ST& rs, int n = 4)
{
rs.a = (STDataType*)malloc(n * sizeof(STDataType));
rs.top = 0; rs.capacity = n;
}
//修改栈顶元素
int& STTop(ST& rs)
{
assert(rs.top > 0);
return rs.a[rs.top-1];
}
这里我用的返回值不是int,而是int&,这里我用int&接收返回值的作用就是我们可以轻松的修改栈顶元素。我们返回的是临时变量。我们可以在main函数里修改栈顶的值。
而如果我们用int接收的话,再这样写就是错误的:
1.4const引用
const int a = 0;
const int& b = a;
因为我在创建变量a的时候,我用了const修饰,我本质是希望a不被修改的,但是如果我给a取了一个别名,这个别名可以用来修改a的值,这是不合理的。这里就是我们所说的权限放大(本来不能改变a,后来我加了一个别名就可以改变a了,这就是权限放大)。
如果我非要这样写,就会报错:
1.5引用和指针的关系
二.inline
inline int Add(int x, int y)
{
int ret = x + y;
ret += 1;
ret += 1;
ret += 1;
return ret;
}
int main()
{
// 可以通过汇编观察程序是否展开
// 有call Add语句就是没有展开,没有就是展开了
int ret = Add(1, 2);
cout << Add(1, 2) * 5 << endl;
return 0;
}
下面就是汇编代码:
不用inline的汇编代码是这样的:
三.nullptr
说到nullptr就肯定会联想到NULL,NULL其实是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:
#ifndef NULL
#ifdef __cplusplus
#define NULL
#else
#define NULL ((void *)0)
#endif
#endif
先来看一段代码:
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;
}
这里最终打印的结果就是:
我们看到这里的传参的虽然是NULL但是打印出来的是与0相同的值。
(1)C++中NULL可能被定义为字面常量0,或者C中被定义为无类型指针(void*)的常量。不论采取何定义,在使用空值的指针时,都不可避免的会遇到⼀些麻烦,本想通过f(NULL)调用指针版本f(int*)函数,但是由于NULL被定义成0,调用了f(int x),因此与程序的初衷相悖。即使是f((void*)NULL)(这样的话,传上面的哪个函数都不匹配),调用会报错。
值得一提的是,在C语言中void*被允许隐式类型转换其他类型的指针,比如在C语言中这样写代码是可以的:
void* p1 = NULL;
int* p2 = p1;
但是在C++里就必须把p1强制转化成int*
(2)C++11中引入nullptr,nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字面量,它可以转换成任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,而不能被转换为整数类型。
这个nullptr就可以任意的转化成其他的类型的指针,把它放到这题上面就可以出现不一样的结果:
到这里,我们对C++基本上有了一定的认识,后面我会继续更新C++的内存。感谢大家的观看,若有错误还请多多指出。