3.c++引用浅谈
[C++] 一篇带你搞懂引用(&)-- C++入门(3)(带底层分析)
C++:“引用”的多种使用情况
C++为什么要用引用而不是指针
C++ 引用 是否占内存和使用
c++头文件重复引用问题
c++当中为什需要引用
如何理解c++中的引用折叠?
【C++基础学习笔记】三、C++中的引用(1)
================================
3.c++引用浅谈
引用reference是一段内存空间的别名
引用规则
1 引用,是一种关系型声明,而非定义。不能独立存在,必须初始化,且与原类型保持一致,且不分配内存。
2 声明关系,一经声明,不可变更。
3 可对引用,再次引用。多次引用的结果,是某一变量具有多个别名,多个别名间是平行关系。
4 辨别引用与其它,&符号前有数据类型时,是引用,其它皆为取地址或按位与。
#include <iostream>
using namespace std;int main()
{
int a=1, b=2;
int& ra = a;
// int &ra = b; //错误,不可更改原有的引用关系
// float &rb = b; //错误,引用类型不匹配
cout << &a <<" " << &ra << endl; //变量与引用具有相同的地址。
int& rra = ra; //可对引用再次引用,表示 a 变量有两个别名,分别是rra 和ra
return 0;
}
引用目的,就是取代指针传参。C++引入引用后,可以用引用解决的问题。避免用指针来解决。
#include <iostream>
using namespace std;void swapByValue(int a, int b)
{
int tmp;
tmp = a;
a = b;
b = tmp;}
void swapByPtr(int* a, int* b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;}
void swapByRef(int& a, int& b)
{
int tmp;
tmp = a;
a = b;
b = tmp;}
int main()
{
int a = 3, b = 5;
swapByValue(a, b); //传值不能交换值,产生了一个副本,交换的只是副本的值,原来的值没发生改变
cout << "a = " << a << " b = " << b << endl;
int c = 6, d = 7;
swapByPtr(&c, &d); //指针可以交换值
cout << "c = " << c << " d = " << d << endl;
int e = 8, f = 9;
swapByRef(e, f); //引用可以交换值
cout << "e = " << e << " f = " << f << endl;
return 0;}
引用的从宏观上可以理解为,扩展了变量的作用域,传参后,就像在本地解决问题一样。
避免了传 n 级指针,解决 n-1 级的问题,即平级内解决问题。
引用的提高
1.指针的引用-有,引用的指针-无
#include <iostream>
using namespace std;int main()
{int* p;
int*& rp = p;
// int&* prp = &rp; //“prp”: 指向引用的指针非法
return 0;
}
引用的本质是指针,C++对裸露的内存地址(指针)作了一次包装。再对引用取地址,建立引用的指针没有意义。
2.指针的指针-有,引用的引用-无
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{int* p;
int** pp = &p;//可以为引用再次声明引用,但不可建立引用的引用。避免了指针调计的"失误"
int a;
int& rx = a;
int& ry = a;
int& rz = ry;//int&& rrz = rz; //引用的引用,是不存在的,C++11 赋于它新的意义,右值引用
return 0;
}
//# 右值引用
注意,虽然 C++98/03 标准不支持为右值建立非常量左值引用,但允许使用常量左值引用操作右值。也就是说,常量左值引用既可以操作左值,也可以操作右值,例如:
int num = 10;
const int &b = num;
const int &c = 10;我们知道,右值往往是没有名称的,因此要使用它只能借助引用的方式。
这就产生一个问题,实际开发中我们可能需要对右值进行修改(实现移动语义时就需要),显然左值引用的方式是行不通的。
为此,C++11 标准新引入了另一种引用方式,称为右值引用,用 "&&" 表示。
话说,C++标准委员会在选定右值引用符号时,既希望能选用现有 C++ 内部已有的符号,还不能与 C++ 98 /03 标准产生冲突,最终选定了 2 个 '&' 表示右值引用。
需要注意的,和声明左值引用一样,右值引用也必须立即进行初始化操作,且只能使用右值进行初始化,比如:
int num = 10;
//int && a = num; //右值引用不能初始化为左值
int && a = 10;和常量左值引用不同的是,右值引用还可以对右值进行修改。例如:
int && a = 10;
a = 100;
cout << a << endl;程序输出结果为 100。
.3.指针数组-有,引用数组-无
#include <iostream>
using namespace std;int main()
{int a, b, c;
int* pArr[] = { &a,&b,&c };
//int & rArr[] = {a,b,c}; //err int & *
return 0;
}
4.数组的引用
#include <iostream>
using namespace std;int main() {
int array[5];//数组名的两重性
int* const& pr = array; // int * &
printf("sizeof(pr) = %d\n", sizeof(pr)); //4int(&ra)[5] = array;
printf("sizeof(ra) = %d\n", sizeof(ra)); //20
return 0;
}
挺有意思的一个例子
#include <iostream>
using namespace std;int main()
{
const int var = 7;
int& Rvar = (int&)var;
Rvar = 9;
cout << var << endl;
cout << Rvar << endl;
return 0;
}
变量监控
控制台打印
constexpr:c++关键字,常量的概念,可以提高性能
#include <iostream>
using namespace std;constexpr int func(int a)
{
a = 3;
//cout << a << endl; //取消注释,会报错
return a;
}int main()
{
constexpr int a = func(1);
cout << a << endl;
return 0;
}
————————————————
版权声明:本文为CSDN博主「天蝎座的程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiaoduan6/article/details/124145080
C++:“引用”的多种使用情况
引用
引用就是操作同一块地址空间,是变量的“别名”,而不是单独的变量,不会单独分配存储单元,所以操作一个变量,引用的变量也会跟着变化;
变量不仅可以有多个引用,还可以多重引用。但需要注意的是,定义时必须初始化,且只能作为一个变量的别名不能再成为其他变量的别名(貌似也没有赋值语句能实现);
引用不是指针,但和指针常量很类似。
不能建立引用的数组,因为它是一系列元素的组合,但是可以建立数组的引用。
#include <iostream>
using namespace std;
void func(int (&array)[3])
{
cout << array[1] << endl;
}
int main()
{
int val;
int& r_val = val;
int& r_val2 = val; // 一个变量可以有多个引用
int& rr_val = r_val; // 多重引用
// int& rr_val2; // 错误:定义时必须初始化
r_val2 = 10;
cout << "val = " << val << " addr: " << &val << endl;
cout << "r_val = " << r_val << " addr: " << &r_val << endl;
cout << "r_val2 = " << r_val2 << " addr: " << &r_val2 << endl;
cout << "rr_val = " << rr_val << " addr: " << &rr_val << endl;cout << "***************************" << endl;
int val2 = 12;
rr_val = val2; // 这是赋值,并非作为val2的别名
cout << "val2 = " << val2 << " addr: " << &val2 << endl;
cout << "rr_val = " << rr_val << " addr: " << &rr_val << endl;cout << "***************************" << endl;
int array[3] = {1, 2, 3};
func(array); // 正确:数组的引用 (函数参数,可以对比一下指针)
// int& r_array[3] = array[3]; // 错误:引用的数组return 0;
}/*
输出结果:
val = 10 addr: 0x6ffdf4
r_val = 10 addr: 0x6ffdf4
r_val2 = 10 addr: 0x6ffdf4
rr_val = 10 addr: 0x6ffdf4
***************************
val2 = 12 addr: 0x6ffdf0
rr_val = 12 addr: 0x6ffdf4
***************************
2
*/
常引用
前面说到引用是一个“别名”,修改其中一个另外一个也会跟着变化,所以如果不希望引用影响原变量,可以定义为常引用。
int val;
const int& r_val = val;
val = 1; // 正确
r_val = 2; // 错误,有const限定
另外,加上const之后引用的类型可以不同,如:
double d = 3.1415926;
const int &i = d;
加上const还可以引用常量:
const double& d = 12.3;
double& d = 12.3; // 错误,需要加上const限定
引用作为参数
引用作为函数的参数可以改变原本的值,因为函数内部操作引用就相当于操作原变量。
#include <iostream>
using namespace std;
void swap1(int a, int b); // 值传递
void swap2(int *a, int *b); // 指针传递
void swap3(int& a, int& b); // 引用传递int main()
{
int a, b;
a = 100; b = 200;
swap1(a, b);
cout << "a = " << a << " b = " << b << endl;
a = 100; b = 200;
swap2(&a, &b);
cout << "a = " << a << " b = " << b << endl;
a = 100; b = 200;
swap3(a, b);
cout << "a = " << a << " b = " << b << endl;return 0;
}void swap1(int a, int b)
{
int temp = a;
a = b;
b = temp;
}void swap2(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}void swap3(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
引用作为返回值
函数可以返回一个引用,可以免去编译器拷贝返回值这一步骤。但需要注意的是,返回的引用不能是局部自动变量,因为函数结束后栈的内存就被自动释放掉了,和返回指针类似。
#include <iostream>
using namespace std;
int array1[10] = {0};
int &func1(int i)
{
return array1[i]; // 返回的引用不能是局部自动变量
}/* 出错,函数运行结束局部自动变量占用的栈内存被释放了
int &func2(int i)
{
int array2[10] = {0};
return array[i];
}
*/int main()
{
func1(3) = 3; // 这里可以被赋值
cout << "array1[3]: " << array1[3] << endl; // 输出结果:array1[3]: 3return 0;
}
————————————————
版权声明:本文为CSDN博主「R-QWERT」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44498318/article/details/105950034
[C++] 一篇带你搞懂引用(&)-- C++入门(3)(带底层分析)
问题引入
在我们日常的生活中每个人都或多或少存在一个"外号",例如《西游记》中孙悟空就有诸多外号:美猴王,孙行者,齐天大圣等等。那么在C++中,也可以给一个已经存在的变量取别名,这就是引用。
那么接下来深入来探讨一下引用
目录
1.引用的概念
1.1引用的表示方法
1.2引用特性
1.3常引用 引用权限
1.4引用的使用场景
1.4.1做参数
1.4.2做返回值
传值的底层过程:
引用导致野指针:
1.5值和引用作为返回值类型的性能比较
1.6引用和指针的区别
。。。。。。省略。。。。。。
————————————————
版权声明:本文为CSDN博主「小白又菜」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_58325487/article/details/124691945
C++为什么要用引用而不是指针
引用和指针的不同:
(1)指针在定义的时候可以不初始化,但引用在定义的时候必须初始化。
(2)指针可以为空,引用不能为空。
(3)指针可以改变他的指向的对象,但引用不可以。
(4)引用的大小是所指向的变量的大小,因为引用只是一个别名而已;指针是指针本身的大小,4个字节。
(5)引用比指针更安全。
由于不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,因此引用很安全。
对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL,所以不安全。
const 指针虽然不能改变指向,但仍然存在空指针,并且有可能产生野指针(即多个指针指向一块内存,free掉一个指针之后,别的指针就成了野指针)。
————————————————
版权声明:本文为CSDN博主「ZJE_ANDY」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u014453898/article/details/54892110
C++ 引用 是否占内存和使用
引用是什么? 大家都知道它就是个别名!
http://blog.csdn.net/webscaler/article/details/6577429 (说是指针常量更正确)
引用的和指针的区别
引用的使用规则:
a:引用在创建的时候必须被初始化;指针可以不初始化(指针使用时必须先判断是否为空值
b:引用必须与一个确定的合法内存单元相关联,不存在NULL引用且不可以使用!; 而指针可以指向NULL
c:一旦引用初始化后,就不能改变引用所指向的变量; 指针可以改变指向别的对象
d: 引用只是别名,不占内存; 指针占内存,它是一个变量
这里说引用不占内存,上面又说是指针常量(占内存),那不是先后矛盾了吗?
其实引用是否占内存标准中并没有规定,可以视它为指针常量,并且在编译器优化后它不占内存
引用作函数的参数:
引用作参数时不产生引用指向的对象的副本,节省时间和空间,且能够直接对指向的对象进行操作,可以达到指针同样的效果,但是使用方法更简洁,不易错
引用作为函数的返回值:
不能返回函数内部临时对象的引用
当需要返回的对象作为左值时需要返回引用 比如重载操作符 [ ] 时就必须使用引用 ,例 str[3] = 'a';
c++当中为什需要引用
为什么需要引入引用?
- 当初加入“引用”这个语言特性的契机是运算符重载。为了让运算符重载的语法能够更加接近内建的运算符,需要能够让一个函数返回一个左值,通俗的讲就是要能够对一个函数的返回值赋值。
- 到了今天,引用除了原始用途之外,实际中最主要的用途是以==常量引用==来修饰函数的输入参数
- ==输入参数,在函数内部不会被改变,此时用常量引用就是最合适的==
其他说法
- 代替指针:引用比指针更加直观,a+b显然比(*a)+(*b)更易懂
- 防止指针引起一些错误:==引用是不可能为空的==,一定确定一个引用,它的目标是不可以被改变的。如果一个类里面有个引用成员,它必须在构造的时候初始化,用它的时候,不必,也不能对它进行判空
- 可以减少不必要的构造析构的开销
参数传递
void Func(Foo foo);
//这个定义的参数,会进行一次拷贝构造,创建一个临时对象传入Func函数
//效率低下浪费资源
void Func(Foo* foo);
//这是C的做法,把指针穿进去,免去调用拷贝构造函数创建临时对象的消耗
//但是引入了新的问题,foo可能为NULL,因此函数里不得不加上判断以免出现空指针调用问题
void Func(const Foo& foo);
//这是C++推荐做法,和指针传递性能一样,但免除了空指针的风险
//作为参数传递时引用和const总是一起出现的
- 如果直接返回类对象同样的会创建临时对象带来开销,如果返回指针则调用者需要判断是否为空,而返回引用则可以非常高效的直接使用
- 引用更加符合面向对象和隐藏实现细节的原则,而指针是更底层的机制。
- ==实际上引用一般不会在同一作用域内被创建==,而是作为参数或者返回值
在引用出现以前,C程序员如果不想作值传递,只能用指针。指针的缺点主要有:
- 指代不直接。要通过->或者*来访问,要多打字而且不直观;
- 可以为空。所以当指代对象不能为空时,对指针要做null检查;
- 可以不赋初值。所以容易出错忘记赋值(现在编译器在警告方面好了很多)
- 指针可以被更改指代对象,更灵活也更容易出错
引用和指针在某些时候也有不同的程序行为,比如dynamic_cast如果失败,指针转换返回null,而引用转换会出bad_cast异常。
注意
- 因为引用不是对象,因此不能定义引用的指针。
- 引用本身不是一个对象,因此不能定义引用的引用。
引用的底层实现方式
- 引用变量在功能上等于一个指针常量,即一旦指向某一个单元就不能在指向别处
- 在底层,引用变量由指针按照指针常量的方式实现。
- 引用变量本身(以r为例)==不允许寻址==,&r返回的是被引用对象的地址,而不是变量r的地址(==r的地址由编译器掌握,程序员无法直接对它进行存取==),被引用对象直接用r表示。
- 凡是使用了引用变量的代码,都可以转换成使用指针常量的对应形式的代码,只不过书写形式上要繁琐一些。反过来,由于对引用变量使用方式上的限制,使用指针常量能够实现的功能,却不一定能够用引用来实现。
- ==数组元素允许是指针常量,却不允许是引用==。C++语言机制如此规定,原因是避免C++语法变得过于晦涩。加入定义一个“引用的数组”,那么array[0]=8;这条语句该如何理解?是将数组元素array[0]本身的值变成8呢,还是将array[0]所引用的对象的值变成8呢?对于程序员来说,这种解释上的二义性对正确编程是一种严重的威胁,毕竟程序员在编写程序的时候,不可能每次使用数组时都要回过头去检查数组的原始定义。
- C++语言规定,引用变量在定义的时候就必须初始化,也即是将引用变量与被引用对象进行绑定。而这种引用关系一旦确定就不允许改变,直到引用变量结束其生命期。这种规定是在高级语言的层面上,由C++语言和编译器所做的检查来保障实施的。
理解:引用本身就是一个编译器管理的变量。底层使用的是指针进行实现。类似于常量指针。
————————————————
版权声明:本文为CSDN博主「zhc_24」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhc_24/article/details/81183071
如何理解c++中的引用折叠?
引用折叠(reference collapsing),其实是结合万能引用(又称为转发引用而来的)
【C++基础学习笔记】三、C++中的引用(1)
摘录要点:
。。。。。。省略。。。。。。
4、类型不同时的强制引用
错误引用:
double a = 12.2;
int& b = a; //会报错
正确引用:
double a = 12.2;
const int& b = a;
以上3和4就涉及一个比较重要的概念:以往我们以为赋值啥的就是直接把值给了对方,但是这里面涉及一个中间变量的操作
如图所示,涉及类型转换时,b不是原来变量a的别名,而是需要转换类型涉及的临时int型的中加变量的别名,这个中间临时变量有常性,需要const进行修饰。
其实在涉及类型转换,如int赋值给double,int与char类型比较等等都会涉及中间变量,之前所学的整形提升也是将需要提升的变量整形提升到一个中间变量再拿这个中间变量去比较或者运算而不是拿原来的那个变量来比较或者运算。
5、常引用在函数返回时的应用
————————————————
版权声明:本文为CSDN博主「Go 鹏ya」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_42961603/article/details/120518127