初识c++(引用,inline,nullprt)
一、引用
1、定义
引用不是新定义⼀个变量,而是给已存在变量取了⼀个别名,编译器不会为引用变量开辟内存空间,
它和它引用的变量共用同⼀块内存空间。
类型& 引用别名 = 引用对象;
#include<iostream>
using namespace std;
int main()
{
int a = 0;
// 引⽤:b和c是a的别名
int& b = a;
int& c = a;
// 也可以给别名b取别名,d相当于还是a的别名
int& d = b;
++d;
// 这⾥取地址我们看到是⼀样的
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;
return 0;
}
示例图片:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZzlXcNLz-1720530489867)(https://i-blog.csdnimg.cn/direct/9193e7c2a59d4a00aba795ae80f65b43.png)]
2、特性
1、引用在定义时必须初始化
2、一个变量可以有多个引用
3、引用一旦引用一个实体,再不能引用其他实体(这于c的指针不同,指针可以改变指向的空间,而引用不行)。
#include<iostream>
using namespace std;
int main()
{
int a = 10;
// 编译报错:“ra”: 必须初始化引⽤
//int& ra;
int& b = a;
int c = 20;
// 这⾥并⾮让b引⽤c,因为C++引⽤不能改变指向,
// 这⾥是⼀个赋值
b = c;
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
return 0;
}
3、使用
引用在实践中主要是于引用传参和引用做返回值中减少拷贝提高效率和改变引用对象时同时改变被
引用对象。
void Swap(int* rx, int* ry)
{
int tmp = *rx;
*rx = *ry;
*ry = tmp;
}
int main()
{
int x = 0, y = 1;
cout << x << " " << y << endl;
Swap(&x, &y);
cout << x << " " << y << endl;
return 0;
}
我们在没有学习c++时实现两个数据的交换是通过指针方式来实现的,而我们知道形参是实参的一份临时拷贝,在调用函数时就会经行拷贝从而减少效率。但是,如果是c++用引用的话,rx,ry就是x,y不用再经行拷贝。
void Swap(int* rx, int* ry)
{
int tmp = *rx;
*rx = *ry;
*ry = tmp;
}
int main()
{
int x = 0, y = 1;
cout << x << " " << y << endl;
Swap(&x, &y);
cout << x << " " << y << endl;
return 0;
}
同时如果我们再日常使用中碰见修改栈顶元素的值时,我们没有学c++时我们会这么做(top为指向栈顶空间的下一个元素)。
void STModityTop(ST& rs, int x)
{
rs.a[rs.top-1] = x;
}
但是我们可以直接将top-1的数据的引用作为返回值,返回到主函数经行修改。
int& STTop(ST& rs)
{
assert(rs.top > 0);
return rs.a[rs.top-1];
}
这里要格外注意一点:引用不能引用函数中局部变量的数据,会造成类似野指针的问题。
4、const引用
可以引用一个const对象,但是必须用const引用。const引用也可以引用普通对象,因为对象的访
问权限在引用过程中可以缩小,但是不能放大。
不过需要注意的是类似 int& rb = a3; double d = 12.34; int& rd = d; 这样⼀些场景下a3的和结果保
存在一个临时对象中, int& rd = d 也是类似,在类型转换中会产生临时对象存储中间值,也就是说,
rb和rd引用的都是临时对象,而C++规定临时对象具有常性,所以这里就触发了权限放大,必须要用常
引用才可以。
所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象,C++中
把这个未命名对象叫做临时对象。(临时变量具有常性)
在 C++ 中,临时变量通常会在以下几个情况下创建:
1、函数返回值:当函数返回一个简单类型(如 int、float 等),如果没有显式指定返回类型,编译器会隐式
地创建一个临时局部变量来存储返回值。
2、参数传递:当函数接受简单类型的参数时,实参和形参之间的值会通过拷贝构造函数复制给临时变量。如
果是引用或指针,则不会创建新的临时变量。
3、表达式求值:在算术表达式、逻辑表达式等计算过程中,可能会创建临时变量来存储中间结果。
4、赋值操作:在赋值语句 a = b
中,如果 a 和 b 类型不匹配或其中一个为常量,系统会生成临时变量用于
交换它们的值。
5、括号展开:当使用括号改变运算顺序时,例如 (a + b) * c
,会创建临时变量来存储 (a + b)
的结果。
6、强制类型转化。强转的结果会放在一个临时变量中。
int main()
{
const int a = 10;
// 编译报错:error C2440: “初始化”: ⽆法从“const int”转换为“int &”
// 这⾥的引⽤是对a访问权限的放⼤
//int& ra = a;
// 这样才可以
const int& ra = a;
// 编译报错:error C3892: “ra”: 不能给常量赋值
//ra++;
// 这⾥的引⽤是对b访问权限的缩⼩
int b = 20;
const int& rb = b;
// 编译报错:error C3892: “rb”: 不能给常量赋值
//rb++;
return 0;
}
#include<iostream>
using namespace std;
int main()
{
int a = 10;
const int& ra = 30;
// 编译报错: “初始化”: ⽆法从“int”转换为“int &”
// int& rb = a * 3;
const int& rb = a*3;
double d = 12.34;
// 编译报错:“初始化”: ⽆法从“double”转换为“int &”
// int& rd = d;
const int& rd = d;
return 0;
}
5、引用和指针的关系
C++中指针和引用就像两个性格迥异的亲兄弟,指针是哥哥,引用是弟弟,在实践中他们相辅相成,功
能有重叠性,但是自己的特点,互相不可替代。
• 语法概念上引用是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。
• 引用在定义时必须初始化,指针建议初始化,但是语法上不是必须的。
• 引用在初始化时引用⼀个对象后,就不能再引用其他对象;而指针可以在不断地改变指向对象。
• 引用可以直接访问指向对象,指针需要解引用才是访问指向对象。
• sizeof中含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下
占4个字节,64位下是8byte)
• 指针很容易出现空指针和野指针的问题,引用很少出现,引用使用起来相对更安全⼀些。
补:引用的底层是指针,但是我们理解的时候不能按照指针取理解,要分开去理解:下面的反汇编语言
说明了为什么引用底层是指针:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JfPpuIxS-1720530489870)(https://i-blog.csdnimg.cn/direct/631c74346fb34131bf6b32c0fc9a02ef.png)]
可见两者反汇编语言是一模一样的。
二、inline
• 用inline修饰的函数叫做内联函数,编译时C++编译器会在调用的地方展开内联函数,这样调用内联
函数就需要建立栈帧了,就可以提高效率。
• inline对于编译器而言只是⼀个建议,也就是说,你加了inline编译器也可以选择在调用的地方不展
开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。inline适用于频繁
调用的短小函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。
• vs编译器 debug版本下面默认是不展开inline的,这样方便调试,debug版本想展开需要设置⼀下
以下两个地方。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K7ejOcw4-1720530489871)(https://i-blog.csdnimg.cn/direct/c22946899f8f49209961f0d1126c0748.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lWJd46yt-1720530489871)(https://i-blog.csdnimg.cn/direct/2f53c62f61584bde97021cdacea4ef44.png)]
• inline不建议声明和定义分离到两个文件,分离会导致链接错误。因为inline被展开,就没有函数地
址,链接时会出现报错。
如果想观察是否展开可以看反汇编下是否有call指令即可,这里不再演示。
#include<iostream>
using namespace std;
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;
}
三、nullptr
我们先看看c语言中定义的宏:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
按照这个我们在c++中写出下面的代码:
#include<iostream>
using namespace std;
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被定义成0,调⽤了f(intx),因此与程序的初衷相悖。
f(NULL);
return 0;
}
我们可以发现两个都为第一个函数,这是因为在c语言中 NULL可以自动强转为任何类型的指针,所以本质上我们在使用malloc时不手动强制转化也是可以的,而c++不行:
#include<iostream>
using namespace std;
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被定义成0,调⽤了f(intx),因此与程序的初衷相悖。
f(NULL);
f((int*)NULL);
// 编译报错:error C2665: “f”: 2 个重载中没有⼀个可以转换所有参数类型
// f((void*)NULL);
return 0;
}
• C++中NULL可能被定义为字面常量0,或者C中被定义为无类型指针(void*)的常量。不论采取何种
定义,在使用空值的指针时,都不可避免的会遇到⼀些麻烦,本想通过f(NULL)调用指针版本的
f(int)函数,但是由于NULL被定义成0,调用了f(int x),因此与程序的初衷相悖。f((void)NULL);*
调用会报错。
• C++11中引入nullptr,nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字面量,它可以转换
成任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被
隐式地转换为指针类型,而不能被转换为整数类型。
#include<iostream>
using namespace std;
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被定义成0,调⽤了f(intx),因此与程序的初衷相悖。
f(NULL);
f((int*)NULL);
// 编译报错:error C2665: “f”: 2 个重载中没有⼀个可以转换所有参数类型
// f((void*)NULL);
f(nullptr);
return 0;
}
#include<iostream>
using namespace std;
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被定义成0,调⽤了f(intx),因此与程序的初衷相悖。
f(NULL);
f((int*)NULL);
// 编译报错:error C2665: “f”: 2 个重载中没有⼀个可以转换所有参数类型
// f((void*)NULL);
f(nullptr);
return 0;
}
原文地址:https://blog.csdn.net/suiyi_freely/article/details/140306407
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!