引用
引用基本语法
引用,就是对同一个地址起个别名。
同一块地址,有不同的名称,就像同一个人,有不同的昵称。
所以,修改时还是两者修改都一样。
示例代码:
void cite(){
int a = 10;
int &b = a;
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"a 的地址为:"<<&a<<endl;
cout<<"b 的地址为:"<<&b<<endl;
a = 20;
cout<<"修改a之后——————"<<endl;
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"a 的地址为:"<<&a<<endl;
cout<<"b 的地址为:"<<&b<<endl;
b = 30;
cout<<"修改b之后——————"<<endl;
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"a 的地址为:"<<&a<<endl;
cout<<"b 的地址为:"<<&b<<endl;
}
运行结果:
出现乱码的问题,通过这个方法解决。
引用注意事项
①引用与变量的关系是多对一,一个引用只能绑定一个变量,但一个变量可以绑定多个引用。
②引用必须初始化,否则会报错。
③引用后其他变量再赋值,此时代码说明并不是与其他变量进行了绑定,而是其他变量的值赋给当前的引用。
示例代码:
void cite_notice(){
cout<<"\t引用注意事项\t"<<endl;
int a = 10;
int &b = a;
int c = 20;
int &A = a;
// 注意事项①,引用:变量 =n:1,多个引用可以指向同一个变量,但一个引用不能更改其指向的变量。
cout<<"各个变量及其地址分别如下。"<<endl;
cout<<"a = "<<a<<",引用&b=a中,b="<<b<<",引用&A=a中,A="<<A<<"。"<<endl;
cout<<"a地址 = "<<&a<<",引用&b=a中,b地址="<<&b<<",引用&A=a中,A地址="<<&A<<"。"<<endl;
cout<<"c = "<<c<<",c地址 = "<<&c<<"。"<<endl;
// 注意事项②,引用如果出现b = c;的代码,其含义是c的值赋值给了b,而不是b指向了c。
cout<<endl;
b = c;
cout<<"b = c 后,各个变量的值和地址分别如下。"<<endl;
cout<<"a = "<<a<<",引用&b=a中,b="<<b<<",引用&A=a中,A="<<A<<"。"<<endl;
cout<<"a地址 = "<<&a<<",引用&b=a中,b地址="<<&b<<",引用&A=a中,A地址="<<&A<<"。"<<endl;
cout<<"c = "<<c<<",c地址 = "<<&c<<"。"<<endl;
}
运行结果:
引用做函数参数
如果是全局变量,再用值传递的方式是否可以交换值?为什么?——答案是不可以。
实验代码:
cout<<"原先的 a = "<<a<<",b = "<<b<<"。"<<endl;
SwapByValue(a , b);//ab的值定义在全局
cout<<"按照值传递后的 a = "<<a<<",b = "<<b<<"。"<<endl;
void SwapByValue(int first_value , int second_value){
int temp_value = second_value;
second_value = first_value;
first_value = temp_value;
}
作用:函数传参数,可以利用引用让形参修饰实参。
优点:可以简化指针修改实参。
总结:通过引用参数产生的效果同地址传递是一样的,引用语法更加清楚简单。
引用的名称可以和原来的名称一致。引用按照我的理解就是一块地址起了个名字,并且不需要解析(指针地址中的解引用)。
三种方法进行交换。
#include<iostream>
#include<string>
using namespace std;
void SwapByValue(int first_value , int second_value){
int temp_value = second_value;
second_value = first_value;
first_value = temp_value;
}
void SwapByAddress(int * first_address , int * second_address){
// 这里的temp_value仍然是整数类型,因为传入的是地址,地址需要解引用取值,是对其值操作,所以用整数类型
int temp_value = *second_address;
*second_address = *first_address;
*first_address = temp_value;
}
void SwapByCite(int &first_cite , int &second_cite){
// 这里用的是引用方式来交换值,传入的是引用,也就是一个值的地址名。但又可以直接对值操作。
int temp_value = second_cite;
second_cite = first_cite;
first_cite = temp_value;
}
int main(){
int a = 10;
int b = 20;
// 把值作为参数传入到方法中
cout<<"原先的 a = "<<a<<",b = "<<b<<"。"<<endl;
SwapByValue(a , b);
cout<<"按照值传递后的 a = "<<a<<",b = "<<b<<"。\n"<<endl;
int a_address = 10;
int b_address = 20;
// 把地址作为参数传入到方法中
cout<<"原先的 a_address = "<<a_address<<",b_address = "<<b_address<<"。"<<endl;
SwapByAddress(&a_address , &b_address);
cout<<"按照地址传递后的 a_address = "<<a_address<<",b_address = "<<b_address<<"。\n"<<endl;
int a_cite = 10;
int b_cite = 20;
// 把引用作为参数传入到方法中
cout<<"原先的 a_cite = "<<a_cite<<",b_cite = "<<b_cite<<"。"<<endl;
SwapByCite(a_cite , b_cite);
cout<<"按照地址传递后的 a_cite = "<<a_cite<<",b_cite = "<<b_cite<<"。"<<endl;
return 0;
}
运行结果如下:
引用做函数返回值
函数返回值可以是引用,但是返回的变量不能是局部变量引用,同样地不能返回局部变量/局部变量的地址,因为返回这些东西,它们的生命周期太短,会被系统销毁,导致会取乱值。
示例代码:
int & example = ReturnLocalCite();
cout<<"返回局部类型的变量引用,对应值为:"<< example<<"。"<<endl;
cout<<"返回局部类型的变量引用,对应值为:"<< example<<"。"<<endl;
cout<<"返回局部类型的变量引用,对应值为:"<< example<<"。"<<endl;
int & ReturnLocalCite(){
int local_variable = 10;
return local_variable;
}
运行结果:没有显示,手动停止的。
调试结果:
函数返回值为引用时,可以作为左值使用——也就是对函数赋值。
示例代码:
#include<iostream>
#include<string>
using namespace std;
int & ReturnGlobalCite(){
cout<<"\n函数返回值为引用时,可以作为左值的示例函数。"<<endl;
static int global_variable = 10;
return global_variable;//这里为什么不返回&global_variable?因为这样做返回的就是地址了
}
int main(){
//用global_example 定义为int类型的引用,接收函数返回值。
int & global_example = ReturnGlobalCite();
cout<<"返回全局类型的变量引用,对应值为:"<<global_example<<"。"<<endl;
cout<<"返回全局类型的变量引用,对应值为:"<<global_example<<"。"<<endl;
ReturnGlobalCite() = 1000;
cout<<"函数返回值为引用时,左值赋值为1000,此时ReturnGlobalCite() = "<<ReturnGlobalCite()<<"。"<<endl;
cout<<"函数返回值为引用时,左值赋值为1000,此时引用global_example = "<<global_example<<"。"<<endl;
}
运行结果:
通过运行结果可以发现,函数返回值为引用时,可以对其进行复制,且函数返回值赋值给的其他变量也会相应地更改值。
引用本质
引用的本质就是指针常量,首先它是个指针,其次这个指针是常量的,按照我理解就是静态的,指向一个地址后,就不能再更改了。所以,回顾之前的代码。新建一个引用:
int a = 10;int &A = a;
就等于int * const A = &a;
,而对a赋值A=20;
就等于*A=20;
。
总结:C++推荐使用引用,因为更简单,其他的都由编译器完成了。
常量引用
基于引用是一个指针常量,看常量引用说明什么问题。
①引用只能指向一个确定的内存地址,比如
int a = 10;
int &A = a;
这是合法的,因为此时A指向a。但如果int &A = 10;
不行,因为10在内存空间中还没有确定的位置,没有确定的位置也就意味着指针常量不能指向它,因为指针常量指向的是一个确定的地址,并且不能改变。所以,如果在前面加上const
关键字,就可以了,此时的工作原理是,编译器给10
(或其他任意的常量)起了一个变量名,这个变量名开发者不知道,但却能够使得const int &A = 10;
正常执行,因为此时变成了静态指针,指针只能指向这块内存,并且这块内存的值不能修改。
②既如此,它的使用环境一般体现在哪里?体现在不修改其值的情况中。比如有函数只想做打印操作,不想更改其值。就可以新建一个变量,函数的参数值为const常量引用,这样如果在函数体内部出现了修改的操作,就会报错。
示例代码:
#include<iostream>
#include<string>
using namespace std;
void ConstantCite(const int &constant){
cout<<"该常量值为:"<<constant<<"。"<<endl;
}
int main(){
const int &tant = 10;
ConstantCite(tant);
return 0;
}
可以看到,函数参数值为const修饰的引用,不能再在函数体内部以及传递参数时修改其值。同时定义的tant是引用,也不能修改值。
函数的默认参数
函数的默认参数意为在函数定义或声明时就对参数赋值。需要注意两点,①函数参数在声明或者定义时,只能存在于一种情况,要么声明时赋值默认参数,要么定义时声明默认参数,二选一,否则编译器会报错。
以下为第一种情况的说明代码及情况。
#include <iostream>
#include <string>
#include"BlackHorseFunctions.h"
using namespace std;
int PlusVersion1(int a = 10 , int b );//这里就报错了
int main(){
system("chcp 65001");
cout<<PlusVersion1()<<endl;
system("pause");
return 0;
}
int PlusVersion1(int a=11 , int b=21 ){//这里值为多少无所谓,它会报错。
return a+b;
}
情况:
====================[ 构建 | Section2Study | Debug ]==============================
“D:\SetUpSoftWare\JetBrain\Clion\CLion 2023.1.1\bin\cmake\win\x64\bin\cmake.exe” --build D:\Program\C++C\BlackHorseStudy\cmake-build-debug --target Section2Study -j 6
[1/2] Building CXX object CMakeFiles/Section2Study.dir/Section2Study.cpp.obj
FAILED: CMakeFiles/Section2Study.dir/Section2Study.cpp.obj
“D:\SetUpSoftWare\JetBrain\Clion\CLion 2023.1.1\bin\mingw\bin\g++.exe” -g -fdiagnostics-color=always -MD -MT CMakeFiles/Section2Study.dir/Section2Study.cpp.obj -MF CMakeFiles\Section2Study.dir\Section2Study.cpp.obj.d -o CMakeFiles/Section2Study.dir/Section2Study.cpp.obj -c D:/Program/C++C/BlackHorseStudy/Section2Study.cpp
D:/Program/C++C/BlackHorseStudy/Section2Study.cpp:27:5: error: default argument given for parameter 1 of ‘int PlusVersion1(int, int)’
27 | int PlusVersion1(int a =11 , int b =21){
| ^~~~~~~~~~~~
D:/Program/C++C/BlackHorseStudy/Section2Study.cpp:9:5: note: previous specification in ‘int PlusVersion1(int, int)’ here
9 | int PlusVersion1(int a = 10 , int b = 20);
| ^~~~~~~~~~~~
D:/Program/C++C/BlackHorseStudy/Section2Study.cpp:27:5: error: default argument given for parameter 2 of ‘int PlusVersion1(int, int)’
27 | int PlusVersion1(int a =11 , int b =21){
| ^~~~~~~~~~~~
D:/Program/C++C/BlackHorseStudy/Section2Study.cpp:9:5: note: previous specification in ‘int PlusVersion1(int, int)’ here
9 | int PlusVersion1(int a = 10 , int b = 20);
| ^~~~~~~~~~~~
ninja: build stopped: subcommand failed.
第二种情况,在给定函数默认参数时,有一个顺序关系,按照从左到右的顺序,如果左面的参数已赋初始值,那么紧随其后右边的参数也需要赋初始值,否则会报错。以下是第二种情况的说明代码及情况。
#include <iostream>
#include <string>
#include"BlackHorseFunctions.h"
using namespace std;
int PlusVersion1(int a = 10 , int b );//这里就报错了
int main(){
system("chcp 65001");
cout<<PlusVersion1()<<endl;
system("pause");
return 0;
}
int PlusVersion1(int a , int b ){
return a+b;
}
情况:====================[ 构建 | Section2Study | Debug ]==============================
“D:\SetUpSoftWare\JetBrain\Clion\CLion 2023.1.1\bin\cmake\win\x64\bin\cmake.exe” --build D:\Program\C++C\BlackHorseStudy\cmake-build-debug --target Section2Study -j 6
[1/2] Building CXX object CMakeFiles/Section2Study.dir/Section2Study.cpp.obj
FAILED: CMakeFiles/Section2Study.dir/Section2Study.cpp.obj
“D:\SetUpSoftWare\JetBrain\Clion\CLion 2023.1.1\bin\mingw\bin\g++.exe” -g -fdiagnostics-color=always -MD -MT CMakeFiles/Section2Study.dir/Section2Study.cpp.obj -MF CMakeFiles\Section2Study.dir\Section2Study.cpp.obj.d -o CMakeFiles/Section2Study.dir/Section2Study.cpp.obj -c D:/Program/C++C/BlackHorseStudy/Section2Study.cpp
D:/Program/C++C/BlackHorseStudy/Section2Study.cpp:9:35: error: default argument missing for parameter 2 of ‘int PlusVersion1(int, int)’
9 | int PlusVersion1(int a = 10 , int b );
| ~~~~^
D:/Program/C++C/BlackHorseStudy/Section2Study.cpp:9:22: note: …following parameter 1 which has a default argument
9 | int PlusVersion1(int a = 10 , int b );
| ~~~~^~~~~~
ninja: build stopped: subcommand failed.