尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

C++ ・入门篇(二)基础精讲中篇:引用篇

C++ ・入门篇(二)基础精讲中篇:引用篇 目录一.引用1.1引用的定义1.2引用的使用方式1.3规则1.4引用的使用场景1.5引用返回1.5.1引用返回的本质1.5.2优势1.6注意事项二.const引用2.1常见问题2.2const引用可以绑定临时变量2.3函数传参场景三.指针与引用区别3.1区别总结指针是“可以指向别处的地址变量”引用是“绑定后不能换的别名”。指针更灵活但更危险引用更自然但更受限两种不存在谁可以完全替代谁的场景。前言C语言只有指针C新增了引用。引用的本质就是变量的别名。const引用则是在此基础上加上“只读”限制不能通过这个别名修改对象。本期将接续上篇继续为大家带来C基础部分更加深入的内容。一.引用1.1引用的定义引用不是新定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。比如水浒传中宋江的别名“及时雨”李逵的别名“黑旋风”。引用本质就是在给对象取别名。1.2引用的使用方式语法类型引用名原变量。和c语言指针使用相同的符号取地址符。引用在对象前面例int* ba还是取地址引用在类型后面例intrbb变成了引用代码演示#includeiostream using namespace std; int main() { int a 10; int ra a; // ra是a的引用别名ra等价于a int rb a; cout a endl; cout ra endl; cout rb endl; ra 20; // 修改raa同步跟着变 cout a endl; rb 30; cout a endl; return 0; }运行结果结论操作引用就是操作原对象。原因编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。如图从第3个输出我们可以知一个变量可以拥有多个别名。如图rarbrca。1.3规则1引用定义时必须初始化不能只声明不绑定变量否则编译报错。如图2一但绑定对象终身无法更改绑定目标注意不是不能改引用指向变量的值是不能把引用重新绑定到另一个变量此时不是让ra改成绑定b而是把b的值赋值给a此时a1ra仍然绑定的a。3普通引用只能绑定可修改的变量不能绑定常量、临时变量int r 10; // 编译报错10是临时常量普通引用无法绑定原因10是临时变量临时变量具有常性普通引用是非 const相当于把只读的东西交给可修改的别名权限放大C 禁止。1.4引用的使用场景引用在实践中主要是于引用传参和引用做返回值中减少拷贝提高效率和改变引用对象时同时改变被引用对象。部分情况下代替一级指针//指针传递法 void swap(int* x,int* y) { int swp*x; *x*y; *yswp; } //引用传递法 void swap(int rx,int ry) { int swprx; rxry; ryswp; } int main() { int a 5; int b 6; swap ( a , b ) ;//指针传递 swap ( a , b ) ;//引用传递 }1.5引用返回1.5.1引用返回的本质普通返回值是把数据复制一份交出去引用返回是把原对象本身它的别名交出去。函数结束时返回的不是拷贝而是同一块内存的又一个名字。1.5.2优势优势一零拷贝性能高大对象更明显假设某个函数返回都复制一份一百万个元素的顺序表那么拷贝代价将无比巨大。反观引用返回将会一次数据都没有复制。对自定义大结构体等传值返回会产生拷贝构造函数调用引用返回完全免去。优势二直接修改对象内部元素类和对象后期会讲解此处暂且先了解一下就行#include string class Person { std::string name_; int age_ 0; public: std::string name() { return name_; } int age() { return age_; } const std::string name() const { return name_; } }; int main() { Person p; p.name() Alice; // 修改内部 name_ p.age() 18; // 修改内部 age_ }1.6注意事项坑一绝不返回局部变量的引用int func() { int ret 5; return ret; // 坑函数结束 ret已销毁返回的是悬空引用 } int r func(); cout r; // 未定义行为可能输出垃圾值、可能崩溃编译器通常也会警告 同理返回临时对象的引用也是悬空的原因函数执行结束局部变量会被销毁返回的引用变成野引用访问属于未定义行为坑二返回值绑定对象必须活得比调用方久坑三小对象别滥用引用返回int func() { ... } // 返回 int 的引用纯属自找麻烦 int func() { return 5; } // int 拷贝几乎零成本按值返回反而更安全int、char、double、指针这类小东西按值返回的开销可忽略按值更安全省心。二.const引用2.1常见问题const int a10; int raa;发生错误error C2440: “初始化”: 无法从“const int”转换为“int ”该错误就是经典的权限放大const修饰a只读所以不能用一个可以读可以写的别名来引用a不然a权限就放大了解决方法权限不可以放大用一个相同权限的别名ra来引用a。需记住权限只能缩小不能放大const int a10; const int raa;2.2const引用可以绑定临时变量底层逻辑临时对象30、表达式结果a5本身是只读的。const int a10;看到该代码时编译器会做两件事1. 在内存创建临时变量保存 302. 让引用 r 绑定这个临时变量因为 r 带 const不会修改内容不存在权限放大问题语法合法。反过来普通引用绑定临时对象试图拿到一个别名去修改只读临时对象 → 权限放大禁止。2.3函数传参场景const 引用作参数减少拷贝只读不修改实参推荐优先使用void print(const int x) { // x 只读函数内部无法修改x cout x endl; }优点1避免传值的拷贝开销。2兼容普通变量常量临时表达式。未来我们会学习一个东西叫做模板template class T void func (const T val){ }模板的体量较大传值传参拷贝消耗大所以采用引用但是引用又存在权限放大限制即——能传进来的东西非常有限。常数、表达式、隐式类型转换的变量等都不能传递引入const引用就变得格外重要——扩大了传递参数的范围。三.指针与引用区别指针是一个变量里面存放某个对象的地址可以改指向、可以为空、可以做运算。引用是某个已经存在的别名一旦绑定无法更改。3.1区别如下我将以表格的方式将二者串起来进行对比特性指针引用本质对象存放地址别名语言层面不是独立对象初始化可以延迟初始化但未初始化很危险必须初始化必须绑定对象可空性可以是nullptr正常不能为空但可能悬空/空引用属于错误使用能否改绑可以改指向别的对象绑定后不能改绑语法*p解引用x取地址直接当原对象用r取到的是原对象地址sizeof指针本身大小如 4/8 字节被引用类型的大小指针运算支持p1、p、相减等不支持r1是值运算多级支持int**不支持“指向引用的指针”“引用数组”内存指针变量本身占存储语言层面无独立存储编译器可能用指针实现安全性更容易空指针、野指针、越界更安全但仍可能悬空引用代码举例int a 10; int b 20; int* p a; // p 指向 a int r a; // r 是 a 的别名 *p 100; // a 变成 100 r 200; // a 变成 200 p b; // p 改指向 b r b; // 注意不是让 r 改绑到 b而是把 b 的值赋给 a // int r2 b; // 如果想让 r 绑定 b只能新建一个引用总结指针是“可以指向别处的地址变量”引用是“绑定后不能换的别名”。指针更灵活但更危险引用更自然但更受限两种不存在谁可以完全替代谁的场景。
返回列表