【Cpp巩固】引用与指针
笔者在学习Cpp
的过程中老是会对指针和引用使用产生疑问,那本文就来详细对比一下指针和引用的区别。
1.基本的语义差异
- 指针是一个变量,存储的是另一个变量的内存地址,指针可以被重复赋值指向相同类型的其他变量,也可以指向空指针
nullptr
。 - 引用是一个已经存在的变量的别名,并且引用必须在声明的时候就进行初始化,引用一旦定义就和该变量绑定了,作为该变量的别名,就不能指向别的变量。
举个例子:
#include <iostream>
using namespace std;
int main() {
int a = 10;
int* a_ptr = &a; // 声明一个指向a的指针
int& a_ref = a; // 声明一个指向a的引用
cout << "This address of a is :" << &a << endl;
cout << "This address of a_ptr is: " << &a_ptr << endl;
cout << "This address of a_ref is: " << &a_ref << endl;
cout << "a: " << a << " a_ptr: " << *a_ptr << " a_ref: " << a_ref << endl;
*a_ptr += 10; // 通过指针修改变量a的值
cout << "a: " << a << " a_ptr: " << *a_ptr << " a_ref: " << a_ref << endl;
a_ref += 10; // 通过引用修改变量a的值
cout << "a: " << a << " a_ptr: " << *a_ptr << " a_ref: " << a_ref << endl;
return 0;
}
Output:
This address of a is :000000D68E3CF7C4
This address of a_ptr is: 000000D68E3CF7E8
This address of a_ref is: 000000D68E3CF7C4
a: 10 a_ptr: 10 a_ref: 10
a: 20 a_ptr: 20 a_ref: 20
a: 30 a_ptr: 30 a_ref: 30
这个例子中创建了一个变量a
,然后创建了指向a
的指针a_ptr
,同时创建了指向a
的引用a_ref
,通过指针a_ptr
和引用a_ref
我们都能访问并且修改变量a
的值,a_ref
与变量a
的地址完全一样,可以视作a_ref
就是变量a
的另一个别名。
2.使用场景
- 指针可以在需要直接操作内存地址或者需要能够指向
NULL
的常见下使用,例如动态内存管理、数据结构的实现等。 - 引用通常用于函数的参数传递过程中,尤其是传递大对象的时候避免了复制成本,或者当你需要确保传递的参数总是指向一个有效的对象时。
我们可以使用指针来进行动态内存管理,举个例子:
#include <iostream>
int main() {
int size;
std::cout << "Please Enter the Size of Elements: ";
std::cin >> size;
int* array = new int[size]; // 动态分配内存
for (int i = 0; i < size; i++) { // 初始化数组
array[i] = (i + 1);
}
std::cout << "Array Elements: " << std::endl;
for (int i = 0; i < size; i++) {
std::cout << array[i] << " ";
}
delete[] array;
return 0;
}
Output:
Please Enter the Size of Elements: 10
Array Elements:
1 2 3 4 5 6 7 8 9 10
在这个例子中,指针 array
被用来指向一个动态分配的整数数组。通过使用 new
和 delete[]
,我们可以控制内存的分配和释放,这是引用无法完成的任务。
我们可以使用引用来进行大型的参数传递,举个例子:
#include <iostream>
#include <vector>
void proccessVector(const std::vector<int>& vec) {
std::cout << "Vector Elements: " << std::endl;
for (int elem : vec) {
std::cout << elem << " ";
}
return;
}
int main() {
std::vector<int> vec = { 1, 2, 3, 4, 5, 7, 8, 9, 10 };
// 使用引用进行参数传递,避免参数复制
proccessVector(vec);
return 0;
}
在这个例子中,std::vector<int>
是一个可能包含大量数据的对象。通过使用引用,我们传递了一个到 processVector
函数的引用,避免了数据的复制。此外,使用 const
关键字确保函数内部不会修改向量的内容。
3.可变性
- 指针可以指向常量
const int* prt
,或者指针本身可以是常量int* const prt
,或者两者都是const int* const ptr
- 引用通常不称其为"常量引用",但是可以有指向常量的引用
cosnt int& ref
,这意味着不能通过修改其所指向的变量的值
#include <iostream>
int main() {
int x = 10;
int y = 20;
// 指针指向非常量数据
int* ptr = &x;
*ptr = 15;
std::cout << "New value of x: " << x << std::endl;
// 指针指向常量数据
const int* ptrToConst = &y;
// *ptrToConst = 25; // 错误,不能通过指向常量的指针修改数据
// 常量指针
int* const constPtr = &x;
*constPtr = 25;
// constPtr = &y; // 错误,不能修改常量指针的指向
// 常量指针指向常量数据
const int* const constPtrToConst = &y;
// *constPtrToConst = 30; // 错误:不能修改y的值
// constPtrToConst = &x; // 错误:常量指针不能改变指向
return 0;
}
4.语法和便利性
- 指针的操作需要使用
*
(解引用)和->
(成员访问) - 引用的使用就像使用普通变量一样,不需要使用特殊的符号来进行访问
我们编写一个汽车类,然后使用指针和引用两种方式来操作这个汽车类的实例,举个例子:
#include <iostream>
class Car {
public:
void start() {
std::cout << "Car started!" << std::endl;
}
};
int main() {
Car mycar;
Car* carPtr = &mycar;
Car& carRef = mycar;
// 使用指针访问成员函数
carPtr->start();
// 使用引用访问成员函数
carRef.start();
return 0;
}
Output:
Car started!
Car started!