文章目录
一、引用
1.基本类型
一个引用就是某个对象的另一个名字。
格式:&X
int a=3;
int &n=a;
n=5;
cout<<a<<endl;
//5
//另一个名字,相当于a=5,所以原来的a也改变。
注意:必须初始化
int &n;
//error
注意:初始化时,等号的右边必须是一个左值(可以放在赋值操作左边的东西,如变量a是,因为int a=1合法,1不是,y因为int 1=1不合法。)
左值:常数123,return返回值
int &n=1;
//1不是左值,error
#include<iostream>
using namespace std;
int vex()
{
int z=10;
return z;
}
int main()
{
int &x=vex();
//error
return 0;
}
注意:一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
类型不同
PS:常引用可以使之成立。
double a=3.14;
int &b=a;
//error
int &c=(int)a;
//error
2.指针的引用
格式:
类型 *&引用名=指针名
。如int *&q=p
。
注意引用符号在的右边,可以理解为:(类型) &引用名=指针名,即将指针的类型当成类型*
int n=9;
int *p=&n;
int *&q=p;
*q=1;
cout<<n<<' '<<*p<<' '<<*q<<endl;
//1 1 1
3.数组的引用
语法:类型 (&引用名)[数组中元素数量n]=数组名;
int a[3]={1,2,3};
int (&b)[3]=a;
//地址一样
for(int i=0;i<3;i++) cout<<&a[i]<<' '<<&b[i]<<endl;
/*
0x6ffe20 0x6ffe20
0x6ffe24 0x6ffe24
0x6ffe28 0x6ffe28
*/
//改变值
b[2]=119;
cout<<a[2]<<endl;
//119
注意:数组中元素数量n必须写出 ,必须等于被引用的数组的定义时的n
#include<iostream>
using namespace std;
int main()
{
int a[3]={1,2,3};
int (&b)[]=a;
//error
return 0;
}
#include<iostream>
using namespace std;
int main()
{
int a[3]={1,2,3};
int n=3;
int (&b)[n]=a;
//error
return 0;
}
#include<iostream>
using namespace std;
void vex(int (&b)[])
{
//error
}
int main()
{
int a[3]={1,2,3};
vex(a);
return 0;
}
注意:数组中元素数量n必须等于被引用的数组的定义时的n
int a[4]={1,2,3};
int (&b)[3]=a;
//error
int a[3]={1,2,3};
int (&b)[4]=a;
//error
数组中元素数量n可以使用const常数值
int a[3]={1,2,3};
const int n=3;
int (&b)[n]=a;
cout<<b[2]<<endl;
//3
二、引用与函数
引用主要用于函数的参数和返回值的同步改变
1.参数的引用
#include<iostream>
using namespace std;
void vex(int &i)
{
i++;
}
int main()
{
int i=0;
vex(i);
cout<<i<<endl;
//1
return 0;
}
2.返回值的引用
(1)临时变量:普通的函数返回值
如果返回类型不是引用,在调用函数的地方会将函数返回值复制给临时对象。
临时对象:在求解表达式的时候,如果需要一个地方存储其运算结果,编译器会创建一个没命名的对象,这就是临时对象。C++程序员通常用temporary这个术语来代替temporary object。
特性:在C++中所有的临时对象都是const类型。
出现:return返回值,类型转化(强制和隐式)
#include<iostream>
using namespace std;
int vex(int a,int b)
{
return a+b;
}
int main()
{
int a=3,b=4;
cout<<vex(a,b)<<endl;
//7
return 0;
}
(2)不能返回对局部变量的引用:
当函数结束时,局部变量被释放。此时对局部对象的引用就会指向不确定的内存!(虽然有的编译器可以输出正确的结果,但那只是局部变量的内存没有被别的东西占据)。返回指向局部对象的指针也是一样的。
#include<iostream>
using namespace std;
int& vex(int a,int b)
{
int z=a+b;
return z;
}
int main()
{
int a=3,b=4;
cout<<vex(a,b)<<endl;
//warning
return 0;
}
解决方法1:返回引用时,要求在函数的参数中,包含有以引用方式或指针方式存在的,需要被返回的参数。
#include<iostream>
using namespace std;
int& vex(int a,int b,int &z)
{
z=a+b;
return z;
}
int main()
{
int a=3,b=4,z;
cout<<vex(a,b,z)<<endl;
//7
return 0;
}
解决方法2:使用static将其声明为静态变量
#include<iostream>
using namespace std;
int& vex(int a, int b)
{
static int z = a + b;
return z;
}
int main()
{
int a = 3, b = 4;
cout << vex(a, b) << endl;
//7
return 0;
}
三、常引用
总结:
- 用来访问被引用对象的值,而不允许修改时
- 统一处理字符串cstr和string的类型(用隐式强制类型转化),如定义参数时
const string & str
,传参时不仅可以传入string
类型,也可以传入cstr
的字符串类型。
1.类似指针
常引用因不可以改变引用变量b的值,但可以改变被引用的变量a的值,像常指针一样引用变量的值会被该变。
/*可以改变被引用的变量a的值*/
int a=3;
const int&b=a;
a=4;
cout<<a<<' '<<b<<endl;
// 4 4
/*不可以改变引用变量b的值*/
int a=3;
const int&b=a;
// b=4;
#include<iostream>
using namespace std;
struct Point
{
int x;
int y;
};
void changStruct(const Point & p)
{
/*不可修改*/
//!p.x=10;
//!p.y=20;
}
class A
{
public:
int a;
int b;
public:
A(int a,int b):a(a),b(b){
}
};
void changeClass(const A & variable)
{
/*不可修改*/
//!variable.a=10;
//!variable.b=20;
}
int main()
{
Point p;
p.x=1,p.y=2;
A m(1,2);
return 0;
}
2.产生一个临时变量
在C++中所有的临时对象都是const类型的
(1)起到隐式类型转化的作用
起到隐式类型转化的作用,转化后的变量被存储到临时变量里,再赋给左值。
a
是double型,所以在int &b
前加const让其强制类型转化。"sunshine"
是const char[],所以在string & str
前加const让其强制类型转化。
#include<iostream>
#include<string>
using namespace std;
//不加const报错
void vex(const string& str) {
cout << str << endl;
}
int main() {
double a = 3;
/* 非常引用报错 */
// int &b=a;
/* 显示强制类型转化的非常引用也报错 */
// int& b = (int)a;
/* 隐式类型强制转化的常引用 */
const int& b = a;
/* 显示类型强制转化的常引用结果也一样,4 3 */
// const int& b = (int)a;
a = 4;
cout << a << ' ' << b << endl;
// 4 3
//注意不要忽略cstr字符串的类型转化
vex("sunshine");
return 0;
}
与相同类型的常引用不同,强制类型转化的常引用,当被引用的变量的值a发生变化时,引用的变量的值不会发生变化。
double a=3.14;
const int&b=a;
a=0.618;
cout<<a<<' '<<b<<endl;
//0.618 3
//改变a的值,注意b的值不变。
(2)返回值
返回值会在系统中产生一个临时对象来存储它们。
#include<iostream>
#include<string>
using namespace std;
// str是临时变量,函数结束后释放掉,但返回值会在系统中产生一个临时对象来存储。
string vex(){
string str="BeautifulDay";
return str;
}
int main(){
cout<<vex()<<endl;
// BeautifulDay
return 0;
}
3.特殊的常引用
OpenCV里Mat类的常引用:https://blog.csdn.net/sandalphon4869/article/details/102217345
四、高级使用
(2)不能返回函数内部new分配的内存的引用。例如,对于返回函数内部new分配内存的引用,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
(3)可以返回类成员的引用,但最好是const。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
(4)引用与一些操作符的重载。流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << “hello” << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。
(5)在另外的一些操作符中,却千万不能返回引用:±*/ 四则运算符。它们不能返回引用。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一 个静态对象引用。根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。
https://blog.csdn.net/tingzhiyi/article/details/75321112
五、引用的例子
1.交换a,b的值
//交换a,b的值
#include<iostream>
using namespace std;
void vex(int &a,int &b)
{
int temp=a;
a=b;
b=temp;
}
int main()
{
int a=3,b=4;
vex(a,b);
cout<<a<<' '<<b<<endl;
//4 3
return 0;
}
2.一个操作数组的设置元素的函数
数组必须得声明或初始化在setValue()函数前。传参因为n不能设置成可变型,写成3后使用性太狭隘,故不采用。
#include<iostream>
using namespace std;
//数组全局声明或初始化
int arr[3];
int& setValue(int i){
return arr[i];
}
int main(){
for (int i = 0; i < 3; i++){
arr[i]=i;
cout<<arr[i]<<' ';
}
//0 1 2
//改变其中的元素
setValue(0)=9;
for (int i = 0; i < 3; i++){
cout<<arr[i]<<' ';
}
//9 1 2
}
Reference:
https://blog.csdn.net/tingzhiyi/article/details/75321112
https://blog.csdn.net/liudongdong19/article/details/83758830
https://www.cnblogs.com/fly1988happy/archive/2011/12/14/2286908.html