文章目录
- 前言
- 一、引用?
- 1.1 引用的概念和定义
- 1.2 引用的特性
- 1.3 引用的使用
- 1.4 const引用(常引用)
- 1.5 指针和引用的关系
- 二、inline
- 2.1inline概念和定义
- 2.2 inline使用
- 2.3 inline注意事项
- 三.nullptr
- 总结
前言
上一篇文章我们介绍了C++中的命名空间,函数重载,缺省参数等内容,今天我们主要介绍引用,inline和nullptr的内容,也属于C++中比较基础但重要的内容。
一、引用?
1.1 引用的概念和定义
引用(reference)就是C++对C语言的重要扩充。引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。
引用的声明方法:
类型标识符 &引用名=目标变量名;
引用引入了对象的一个同义词。定义引用的表示方法与定义指针相似,只是用&代替了*。C++中为了避免引入太多的运算符,会复用C语言的一些符号,比如如前面的<<和>>,这里引用也和取地址使用了同⼀个符号&,大家注意使用方法角度区分就可以
int main()
{
int a = 10;
int& b = a;//b是a的别名
int& c = b;//c是b的别名
c++;//c++同时b和a都++
cout << a << endl;
cout << b << endl;
cout << c << endl;
return 0;
}
如果我们看他们的地址会发现,他们是一样的
1.2 引用的特性
(1)&在此不是求地址运算,而是起标识作用。
(2)类型标识符是指目标变量的类型。
(3)声明引用时,必须同时对其进行初始化。
(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
//以下写法都是不对的
int main()
{
int& ra;//未初始化
int a = 10;
int b = 20;
int& rb = a;
int& rb = b;//引用多个对象
}
1.3 引用的使用
• 引用在实践中主要是于引用传参和引用做返回值中减少拷贝提高效率和改变引用对象时同时改变被引用对象。
• 引用传参跟指针传参功能是类似的,引用传参相对更方便⼀些。
• 引用和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引用跟其他语言的引用(如Java)是有很大的区别的,除了用法,最大的点,C++引用定义后不能改变指向,Java的引用可以改变指向。
• 使用C++引用替代指针传参,目的是简化程序,避开复杂的指针。
void Swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 10;
int b = 20;
Swap(a, b);
cout << a << endl;
cout << b << endl;
return 0;
}
之前我们用C语言想写一个交换函数需要传递指针才能改变实参,但是在这里,我们传递实参的别名,用引用的方式,不传指针也能直接改变实参。
typedef struct ListNode
{
int val;
struct ListNode* next;
}LTNode, *PNode;
// 指针变量也可以取别名,这⾥LTNode*& phead就是给指针变量取别名
// 这样就不需要⽤⼆级指针了,简化了程序
//void ListPushBack(LTNode** phead, int x)
//void ListPushBack(LTNode*& phead, int x)
1.4 const引用(常引用)
常引用声明方式:const 类型标识符&引用名=目标变量名;
用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性
- 使用const定义的变量只能用使用const定义的别名,无const的变量,别名视情况加const。总的来说就是权限可以缩小但是不能放大。
int a = 10;
const int& ra = a;//引用正确,ra使用const只读不写,访问权限缩小
const int a = 10;
const int& ra = 10;//引用正确,ra和a都是只读不写。
const int a = 10;
int& ra = a;//引用错误,ra可以进行写操作,权限增大
- 需要注意的是类似 int& rb = a * 3; double d = 12.34; int& rd = d; 这样⼀些场景下a*3的和结果保存在⼀个临时对象中, int& rd = d 也是类似,在类型转换中会产生临时对象存储中间值,也就是时,rb和rd引用的都是临时对象,而C++规定临时对象具有常性,所以这⾥就触发了权限放大,必须要用常引用才可以。
int a = 10;
const int& ra = 30;
//int& rb=a*3 错误,a*3属于临时变量需要加const
const int& rb = a*3;//正确
double d = 12.34;
// 编译报错:“初始化”: ⽆法从“double”转换为“int &”
// int& rd = d; 发生截断,相当于把截断后的变量存入一个临时变量,用const修饰
const int& rd = d;//正确
return 0;
所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象,C++中把这个未命名对象叫做临时对象。
1.5 指针和引用的关系
C++中的指针和引用是相互有重合,也相互有不同两个概念。在真实的实践中他们相辅相成,不能相互取代。
- 从内存空间上看,引用只是给原来的变量取了一个别名,其并不开辟内存空间,而指针需要存储一个变量的地址,需要开辟空间。
- 引用必须初始化,指针可以不初始化(只是建议初始化)。
- 引用只能指定一个对象,指针可以改变指向。
- 引用可以直接控制原来的变量,指针需要解引用才能控制原变量。
- sizeof的结果不一样,引用的结果是原变量的大小,指针的结果是地址空间所占的字节个数。
- 指针容易出现野指针,空指针的问题。引用不容易出现这些问题,相对安全。
二、inline
2.1inline概念和定义
inline是C++关键字,在函数声明或定义中,函数返回类型前加上关键字inline,即可以把函数指定为内联函数。这样可以解决一些频繁调用的函数大量消耗栈空间(栈内存)的问题。关键字inline必须与函数定义放在一起才能使函数成为内联函数,仅仅将inline放在函数声明前面不起任何作用。inline是一种“用于实现”的关键字,而不是一种“用于声明”的关键字。
inline的出现一定程度上取代了C语言中宏函数。C中的宏函数经常会因为少加括号等问题而得不到正确的答案。
2.2 inline使用
在使用inline时只需要在定义函数前面加上inline即可
inline int Add(int x, int y)
{
int ret = x + y;
ret += 1;
ret += 1;
ret += 1;
return ret;
}
int main()
{
int ret = Add(1, 2);
cout << Add(1, 2) * 5 << endl;
return 0;
}
2.3 inline注意事项
内联是以代码膨胀复制为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码, 将使程序的总代码量增大,消耗更多的内存空间。以下情况不宜使用内联:
1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。
inline对于编译器而言只是⼀个建议,也就是说,你加了inline编译器也可以选择在调用的地方不展开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。inline适用于频繁调⽤的短小函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。
vs编译器debug版本下面默认是不展开inline的,这样方便调试,debug版本想展开需要设置⼀下以下两个地方。
inline不建议声明和定义分离到两个文件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。
三.nullptr
在C语言中NULL是一个宏,其定义如下
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
C++中NULL可能被定义为字面常量0,或者C中被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到⼀些麻烦,本想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,调用了f(int* x),因此与程序的初衷相悖。f((void*)NULL);调用会报错。
void f(int x)
{
cout << "f(int x)" << endl;
}
void f(int* ptr)
{
cout << "f(int* ptr)" << endl;
}
int main()
{
f(0);
f(NULL);
f((int*)NULL);
f(nullptr);
return 0;
}
C++11中引入nullptr,nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字面量,它可以转换成任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,而不能被转换为整数类型。
总结
以上就是今天的全部内容,主要介绍了一些C++的基础内容,C++的内容还有许多,如果喜欢这篇博客,期待你的一键三连。