C++ 引用
概述
引用(Reference)是C++中的一个重要特性,它为已存在的变量提供了一个别名。引用本质上是对象的另一个名字,通过引用可以直接操作原始对象。引用提供了比指针更安全、更直观的间接访问方式,是现代C++编程的重要工具。
🔗 引用基础
引用的声明和初始化
cpp
#include <iostream>
int main() {
std::cout << "=== 引用基础 ===" << std::endl;
// 1. 基本引用声明
int original = 42;
int& ref = original; // ref是original的引用(别名)
std::cout << "原变量值: " << original << std::endl;
std::cout << "引用值: " << ref << std::endl;
std::cout << "原变量地址: " << &original << std::endl;
std::cout << "引用地址: " << &ref << std::endl;
// 2. 通过引用修改原变量
ref = 100;
std::cout << "通过引用修改后:" << std::endl;
std::cout << "原变量值: " << original << std::endl;
std::cout << "引用值: " << ref << std::endl;
// 3. 引用必须初始化
// int& bad_ref; // 错误!引用必须初始化
// 4. 引用不能重新绑定
int another = 200;
// ref = another; // 这是赋值,不是重新绑定
ref = another; // 将another的值赋给original
std::cout << "赋值后original: " << original << std::endl;
return 0;
}引用 vs 指针
cpp
#include <iostream>
int main() {
std::cout << "=== 引用 vs 指针 ===" << std::endl;
int value = 42;
// 引用方式
int& ref = value;
// 指针方式
int* ptr = &value;
std::cout << "原始值: " << value << std::endl;
// 修改值
ref = 100; // 直接赋值
std::cout << "通过引用修改: " << value << std::endl;
*ptr = 200; // 需要解引用
std::cout << "通过指针修改: " << value << std::endl;
// 语法比较
std::cout << "\n语法比较:" << std::endl;
std::cout << "引用访问: " << ref << std::endl;
std::cout << "指针访问: " << *ptr << std::endl;
// 引用的优势
std::cout << "\n引用优势:" << std::endl;
std::cout << "1. 语法简洁,不需要解引用" << std::endl;
std::cout << "2. 不能为空,更安全" << std::endl;
std::cout << "3. 不能重新绑定,避免错误" << std::endl;
std::cout << "4. 没有指针算术运算" << std::endl;
return 0;
}📋 const引用
const引用的使用
cpp
#include <iostream>
#include <string>
int main() {
std::cout << "=== const引用 ===" << std::endl;
// 1. const引用绑定到变量
int value = 42;
const int& const_ref = value;
std::cout << "const引用值: " << const_ref << std::endl;
// const_ref = 100; // 错误!不能通过const引用修改值
value = 100; // 可以直接修改原变量
std::cout << "修改原变量后const引用值: " << const_ref << std::endl;
// 2. const引用绑定到字面量
const int& literal_ref = 50; // 绑定到临时对象
const std::string& str_ref = "Hello World";
std::cout << "字面量引用: " << literal_ref << std::endl;
std::cout << "字符串引用: " << str_ref << std::endl;
// 3. const引用绑定到表达式结果
int a = 10, b = 20;
const int& expr_ref = a + b; // 绑定到临时对象
std::cout << "表达式引用: " << expr_ref << std::endl;
return 0;
}const引用作为函数参数
cpp
#include <iostream>
#include <string>
#include <vector>
// 使用const引用避免拷贝,同时保证不修改参数
void printString(const std::string& str) {
std::cout << "字符串: " << str << std::endl;
// str += "!"; // 错误!不能修改const引用
}
void printVector(const std::vector<int>& vec) {
std::cout << "向量元素: ";
for (const int& element : vec) {
std::cout << element << " ";
}
std::cout << std::endl;
}
// 计算字符串长度(不修改原字符串)
size_t getStringLength(const std::string& str) {
return str.length();
}
// 查找最大值(不修改原数组)
int findMax(const std::vector<int>& numbers) {
int max_val = numbers[0];
for (const int& num : numbers) {
if (num > max_val) {
max_val = num;
}
}
return max_val;
}
int main() {
std::cout << "=== const引用作为函数参数 ===" << std::endl;
// 字符串示例
std::string message = "Hello C++";
printString(message);
printString("Literal String"); // 可以传递字面量
size_t len = getStringLength(message);
std::cout << "字符串长度: " << len << std::endl;
// 向量示例
std::vector<int> numbers = {5, 2, 8, 1, 9, 3};
printVector(numbers);
int max_num = findMax(numbers);
std::cout << "最大值: " << max_num << std::endl;
return 0;
}🔄 引用作为函数返回值
返回引用的函数
cpp
#include <iostream>
#include <vector>
class NumberContainer {
private:
std::vector<int> numbers_;
public:
NumberContainer(std::initializer_list<int> init) : numbers_(init) {}
// 返回引用,允许修改
int& at(size_t index) {
if (index >= numbers_.size()) {
throw std::out_of_range("索引越界");
}
return numbers_[index];
}
// const版本,返回const引用
const int& at(size_t index) const {
if (index >= numbers_.size()) {
throw std::out_of_range("索引越界");
}
return numbers_[index];
}
size_t size() const { return numbers_.size(); }
void print() const {
for (const int& num : numbers_) {
std::cout << num << " ";
}
std::cout << std::endl;
}
};
// 返回两个数中较大者的引用
int& max(int& a, int& b) {
return (a > b) ? a : b;
}
int main() {
std::cout << "=== 引用作为返回值 ===" << std::endl;
// 1. 容器引用返回
NumberContainer container{10, 20, 30, 40, 50};
std::cout << "原始容器: ";
container.print();
// 通过引用修改元素
container.at(2) = 300;
std::cout << "修改后: ";
container.print();
// 2. 选择性修改
int x = 15, y = 25;
std::cout << "x = " << x << ", y = " << y << std::endl;
max(x, y) = 100; // 修改较大者
std::cout << "修改较大者后: x = " << x << ", y = " << y << std::endl;
// 3. const对象使用const引用
const NumberContainer const_container{1, 2, 3};
std::cout << "const容器第一个元素: " << const_container.at(0) << std::endl;
// const_container.at(0) = 10; // 错误!不能修改const引用
return 0;
}危险的引用返回
cpp
#include <iostream>
// 危险:返回局部变量的引用
int& dangerousFunction() {
int local = 42;
return local; // 危险!返回局部变量的引用
}
// 安全:返回参数的引用
int& safeFunction(int& param) {
return param; // 安全,param来自外部
}
// 安全:返回成员变量的引用
class SafeClass {
private:
int member_;
public:
SafeClass(int value) : member_(value) {}
int& getMember() {
return member_; // 安全,返回成员变量
}
};
int main() {
std::cout << "=== 引用返回的安全性 ===" << std::endl;
// 危险示例(不要这样做)
std::cout << "危险示例(仅作演示):" << std::endl;
// int& dangerous_ref = dangerousFunction(); // 悬挂引用
// std::cout << dangerous_ref << std::endl; // 未定义行为
// 安全示例
int value = 100;
int& safe_ref = safeFunction(value);
std::cout << "安全引用: " << safe_ref << std::endl;
safe_ref = 200;
std::cout << "修改后: " << value << std::endl;
// 成员变量引用
SafeClass obj(300);
int& member_ref = obj.getMember();
std::cout << "成员引用: " << member_ref << std::endl;
member_ref = 400;
std::cout << "修改成员后: " << obj.getMember() << std::endl;
return 0;
}🎯 引用的高级应用
引用与函数重载
cpp
#include <iostream>
class Processor {
public:
// 处理可修改对象
void process(int& value) {
value *= 2;
std::cout << "处理可修改引用: " << value << std::endl;
}
// 处理只读对象
void process(const int& value) {
std::cout << "处理只读引用: " << value << std::endl;
}
};
// 完美转发示例(C++11)
template<typename T>
void forwardToProcess(T&& arg) {
Processor proc;
proc.process(std::forward<T>(arg));
}
int main() {
std::cout << "=== 引用与函数重载 ===" << std::endl;
Processor proc;
// 可修改对象
int mutable_var = 10;
std::cout << "调用前: " << mutable_var << std::endl;
proc.process(mutable_var);
std::cout << "调用后: " << mutable_var << std::endl;
// 只读对象
const int const_var = 20;
proc.process(const_var);
// 临时对象
proc.process(30);
// 完美转发
std::cout << "\n完美转发示例:" << std::endl;
int value = 50;
forwardToProcess(value); // 左值
forwardToProcess(60); // 右值
return 0;
}引用数组和引用的引用
cpp
#include <iostream>
int main() {
std::cout << "=== 引用的限制 ===" << std::endl;
// 1. 引用数组(不存在)
// int& ref_array[3]; // 错误!不能创建引用的数组
// 但可以创建数组的引用
int arr[3] = {1, 2, 3};
int (&array_ref)[3] = arr; // 数组的引用
std::cout << "数组引用: ";
for (int i = 0; i < 3; i++) {
std::cout << array_ref[i] << " ";
}
std::cout << std::endl;
// 2. 引用的引用(需要类型推导)
int value = 42;
int& ref1 = value;
// int& & ref2 = ref1; // 错误!不能直接声明引用的引用
// 但在模板中可能出现引用折叠
auto& ref2 = ref1; // ref2也是int&类型
std::cout << "引用的引用: " << ref2 << std::endl;
// 3. 指向引用的指针(不存在)
// int&* ptr_to_ref; // 错误!不能创建指向引用的指针
// 但可以创建引用到指针
int* ptr = &value;
int*& ref_to_ptr = ptr; // 指针的引用
std::cout << "指针引用指向的值: " << *ref_to_ptr << std::endl;
return 0;
}📚 实际应用示例
链表节点操作
cpp
#include <iostream>
struct ListNode {
int data;
ListNode* next;
ListNode(int value) : data(value), next(nullptr) {}
};
class SimpleList {
private:
ListNode* head_;
public:
SimpleList() : head_(nullptr) {}
~SimpleList() {
while (head_) {
ListNode* temp = head_;
head_ = head_->next;
delete temp;
}
}
void insert(int value) {
ListNode* new_node = new ListNode(value);
new_node->next = head_;
head_ = new_node;
}
// 返回引用允许修改
int& at(int index) {
ListNode* current = head_;
for (int i = 0; i < index && current; i++) {
current = current->next;
}
if (!current) {
throw std::out_of_range("索引越界");
}
return current->data;
}
void print() const {
ListNode* current = head_;
std::cout << "链表: ";
while (current) {
std::cout << current->data << " ";
current = current->next;
}
std::cout << std::endl;
}
};
int main() {
std::cout << "=== 链表引用应用 ===" << std::endl;
SimpleList list;
list.insert(10);
list.insert(20);
list.insert(30);
list.print();
// 通过引用修改节点数据
list.at(1) = 200;
std::cout << "修改索引1后: ";
list.print();
return 0;
}📋 引用最佳实践
引用使用指南
cpp
#include <iostream>
#include <string>
#include <vector>
// 最佳实践示例
class BestPractices {
public:
// 1. 传递大对象使用const引用
static void processLargeObject(const std::vector<std::string>& data) {
std::cout << "处理 " << data.size() << " 个字符串" << std::endl;
}
// 2. 需要修改参数时使用非const引用
static void modifyString(std::string& str) {
str += " - 已修改";
}
// 3. 简单类型可以按值传递
static int addNumbers(int a, int b) { // 不需要引用
return a + b;
}
// 4. 返回引用用于链式调用
BestPractices& setValue(int value) {
value_ = value;
return *this; // 返回自身引用
}
BestPractices& multiply(int factor) {
value_ *= factor;
return *this;
}
int getValue() const { return value_; }
private:
int value_ = 0;
};
int main() {
std::cout << "=== 引用最佳实践 ===" << std::endl;
// 1. 处理大对象
std::vector<std::string> large_data = {
"字符串1", "字符串2", "字符串3", "字符串4"
};
BestPractices::processLargeObject(large_data);
// 2. 修改参数
std::string message = "原始消息";
std::cout << "修改前: " << message << std::endl;
BestPractices::modifyString(message);
std::cout << "修改后: " << message << std::endl;
// 3. 链式调用
BestPractices obj;
obj.setValue(10).multiply(3).multiply(2);
std::cout << "链式调用结果: " << obj.getValue() << std::endl;
// 4. 使用指南
std::cout << "\n引用使用指南:" << std::endl;
std::cout << "✓ 传递大对象时使用const引用" << std::endl;
std::cout << "✓ 需要修改参数时使用引用" << std::endl;
std::cout << "✓ 返回引用实现链式调用" << std::endl;
std::cout << "✓ 简单类型按值传递" << std::endl;
std::cout << "✗ 避免返回局部变量的引用" << std::endl;
std::cout << "✗ 不要创建空引用" << std::endl;
return 0;
}总结
引用是C++提供的重要特性,为间接访问提供了安全高效的方式:
核心特点
- 别名机制:引用是对象的另一个名字
- 必须初始化:声明时必须绑定到对象
- 不能重绑定:一旦绑定无法改变
- 无空引用:比指针更安全
使用场景
| 场景 | 推荐做法 | 原因 |
|---|---|---|
| 函数参数 | const引用 | 避免拷贝,保证安全 |
| 函数返回 | 引用(谨慎) | 支持链式调用 |
| 容器访问 | 引用 | 允许就地修改 |
| 简单类型 | 按值传递 | 性能更好 |
最佳实践
- 传递大对象使用const引用
- 需要修改时使用非const引用
- 避免返回局部变量引用
- 优先使用引用而非指针
- 合理使用const保证安全性
引用让C++代码更加简洁和安全,是现代C++编程不可缺少的工具。