C++中的引用
当变量声明为引用时,它将成为现有变量的替代名称。通过在声明中添加“&”,可以将变量声明为引用
#include<iostream>
using namespace std;
int main()
{
int x = 10;
// ref is a reference to x.
int& ref = x;
// Value of x is now changed to 20
ref = 20;
cout << "x = " << x << endl ;
// Value of x is now changed to 30
x = 30;
cout << "ref = " << ref << endl ;
return 0;
}
修改函数中传递的参数
如果函数收到对变量的引用,则可以修改变量的值。例如,在以下程序变量中使用引用进行交换。
#include<iostream>
using namespace std;
void swap (int& first, int& second)
{
int temp = first;
first = second;
second = temp;
}
int main()
{
int a = 2, b = 3;
swap( a, b );
cout << a << " " << b;
return 0;
}
避免复制大型结构
想象一个必须接收大型对象的函数。如果我们通过它而没有引用,则会创建它的新副本,这会浪费CPU时间和内存。我们可以使用引用来避免这种情况。
struct Student {
string name;
string address;
int rollNo;
}
// If we remove & in below function, a new
// copy of the student object is created.
// We use const to avoid accidental updates
// in the function as the purpose of the function
// is to print s only.
void print(const Student &s)
{
cout << s.name << " " << s.address << " " << s.rollNo;
}
在“每个循环”中,修改所有对象
我们可以在每个循环中使用“引用”来修改所有元素。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vect{ 10, 20, 30, 40 };
// We can modify elements if we
// use reference
for (int &x : vect)
x = x + 5;
// Printing elements
for (int x : vect)
cout << x << " ";
return 0;
}
在For Each循环中,避免对象的复制
当对象很大时,我们可以在每个循环中使用以避免复制单个对象。
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<string> vect{"geeksforgeeks practice",
"geeksforgeeks write",
"geeksforgeeks ide"};
// We avoid copy of the whole string
// object by using reference.
for (const auto &x : vect)
cout << x << endl;
return 0;
}
引用与指针
引用和指针都可用于更改另一个函数内部一个函数的局部变量。当作为参数传递给函数或从函数返回时,它们都还可以用于保存大对象的副本,以提高效率。
尽管有上述相似之处,但引用和指针之间仍存在以下差异。
可以将指针声明为void,但引用永远不能为void
引用没有指针强大
1. 创建引用后,以后就不能再引用另一个对象了;它无法重新放置。这通常是通过指针完成的。
2. 引用不能为NULL。指针通常被设置为NULL,以指示它们没有指向任何有效的对象。
3. 引用必须在声明时进行初始化。指针没有这种限制
由于上述限制,C ++中的引用不能用于实现链接列表,树等数据结构。在Java中,引用没有上述限制,并且可以用于实现所有数据结构。引用在Java中更强大,这是Java不需要指针的主要原因。
引用更安全且更易于使用:
1. 更安全:由于必须初始化引用,因此不太可能存在诸如野生指针之类的野生引用。仍然有引用可能不指向有效位置(请参阅下面的练习中的问题5和6)
2. 易于使用:引用不需要解引用运算符即可访问该值。它们可以像普通变量一样使用。仅在声明时才需要“&”运算符。另外,可以使用点运算符('。')访问对象引用的成员,这与需要箭头运算符(->)访问成员的指针不同。
结合上述原因,在像复制构造函数参数这样的地方很少有不能使用指针的地方。必须使用引用传递副本构造函数中的参数。类似地,必须使用引用来重载某些运算符,例如++。
举个练习例子
例一
#include<iostream>
using namespace std;
int &fun()
{
static int x = 10;
return x;
}
int main()
{
fun() = 30;
cout << fun();
return 0;
}
例二
#include<iostream>
using namespace std;
int fun( int &x)
{
return x;
}
int main()
{
int a = 10;
cout << fun(a);
return 0;
}
例三
#include<iostream>
using namespace std;
void swap(char * &str1, char * &str2)
{
char *temp = str1;
str1 = str2;
str2 = temp;
}
int main()
{
char *str1 = "GEEKS";
char *str2 = "FOR GEEKS";
swap(str1, str2);
cout<<"str1 is "<<str1<<endl;
cout<<"str2 is "<<str2<<endl;
return 0;
}
例四
#include<iostream>
using namespace std;
int main()
{
int x = 10;
int *ptr = &x;
int* &ptr1 = ptr;
}
#include<iostream>
using namespace std;
int main()
{
int *ptr = NULL; // error
int *ptr = &p;
int &ref = *ptr;
cout << ref;
}
例五
#include<iostream>
using namespace std;
int &fun()
{
int x = 10;
return x;
}
int main()
{
fun() = 30;
cout << fun();
return 0;
}
2020/02/20补充
引用:从宏观上可以理解为扩展了变量的作用域,传参后,就像是在本地解决问题一样避免了传n级指针,解决了n-1级的问题,即平级内解决问题。
提高
引用的本质是指针,也就是说C++对裸露的内存地址(指针)做一次包装
1. 指针的引用有,引用的指针无
int *& t1; //ok
int &* t2; //error
2. 指针的指针有, 引用的引用无
指针的指针,即是二级指针,C++为了避免C语言设计指针的“失误”, 避免了引用的引用这种情况。有次也避免了引用的引用的。。。。。。
3. 指针的数组有, 引用的数组无
int* arr[] = {&a, &b, &c}; //ok
int& arr[] = {a,b,c}; //error
常引用
C++中 const 定义的变量称为常变量。变量的形式,常量的作用,用作常量,常量取代#define宏定义。
注意:#define 是预处理的时候替换。。。const是在汇编的时候替换。
常引用的特性
const 的本意,即是不可修改。所以const对象,只能声明为const引用,使其语义保持一致性①。non-const对象,既可以声明为const引用,也可以声明为no-const引用②。声明const引用,则不可以通过const引用修改数据③。
int main(int argc, char** argv){
const int t1 = 10; /* 1 */
const int& t2 = t1;
int b1 = 100; /* 2 */
int &b2 = b1;
const int &b3 = b1;
const int d1 = 100; /* 3 */
d1 = 100; //error
}
临时对象的常引用
临时对象通常指不可以取地址的对象(如:常量),即CPU中计算产生的中间变量能常称为右值。常见临时值有常量,表达式(如:a+b),函数返回值等。
1. 常量
const int& q = 10;
2. 表达式
int a = 3, b = 5;
const int &ret = a+b;
3. 函数返回值
int foo(){
int a = 100;
return a;
}
int main(int argc, char **argv){
int &ra = foo(); //直接赋值给引用是有问题的
const int &ra = foo();
}
4. 类型不同的变量
double d = 3.14;
//产生中间变量
const int& t = d;
//这里哪怕再次修改d的值但是t也不会变,因为const
引用的本质
例子如下:
#include <time.h>
#include <iostream>
using namespace std;
struct typeA{
char a;
};
struct typeB{
char* b;
};
struct typeC{
char& c;
};
int main(){
cout <<sizeof(typeA) << " " <<sizeof(typeB) << " "<<sizeof(typeC) << " "<<endl;
}
/**
结果是:
1 4 4
*/
字节大小跟指针都是一样是4个字节,而且引用必须要初始化,说明什么?
这说明引用的本质可能是指针,必须初始化,可能是常指针。引用可以改变吗?引用一经声明不可改变。
在这里就不得不说说const int *,const int * const和int const *之间的区别了
const int * 等同于 int const *:是指向常量整型的指针。
这就说明我们声明的变量是指向常量整数的指针。实际上,这就意味着指针指向了一个不应修改的值。在这种情况下,const限定符不会影响指针,因此允许该指针指向其他地址。
第一个const关键字可以位于数据类型的任何一侧,因此int const *等同于const int *。
例子下:
#include <iostream>
int main(){
const int q = 5;
int const* p = &q;
//Compilation error
*p = 7;
const int q2 = 7;
//Valid
p = &q2;
return 0;
}
int * const 是指向整型的常量指针。实际上,这意味着指针不应指向其他地址。在这种情况下,常量限定符不会影响整数的值,因此允许更改存储在地址中的值。
例子下:
nclude <stdio.h>
int main(){
const int q = 5;
//Compilation error
int *const p = &q;
//Valid
*p = 7;
const int q2 = 7;
//Compilation error
p = &q2;
return 0;
}
const int* const:指向常量整数的常量指针
这意味着声明的变量是指向常量整数的常量指针。有效地,这意味着常数指针指向常数值。因此,指针既不应指向新地址,也不应更改所指向的值。
第一个const关键字可以位于数据类型的任何一侧,因此const int * const等效于int const * const。
#include <stdio.h>
int main(){
const int q = 5;
//Valid
const int* const p = &q;
//Compilation error
*p = 7;
const int q2 = 7;
//Compilation error
p = &q2;
return 0;
}
本博文部分是根据优快云学院,王桂林老师的课程内容。主要是自己为了学习而记下来的笔记,如有不妥联删
部分参考:https://www.geeksforgeeks.org/difference-between-const-int-const-int-const-and-int-const