第9章 - 引用和动态内存
嗨,朋友!我是长安。
上一章我们学习了指针,这一章我们要学习两个重要的概念:引用和动态内存分配。
引用可以看作是"指针的简化版",而动态内存让我们能够在程序运行时灵活地分配内存。
🔗 什么是引用?
生活中的类比
想象你有一个笔记本:
- 笔记本 = 变量
- 笔记本的别名(昵称) = 引用
比如你的笔记本叫"学习笔记",你也可以叫它"小本本"。两个名字指向同一个笔记本。
编程中的引用
引用就是变量的别名,和原变量共享同一块内存。
int num = 10;
int& ref = num; // ref 是 num 的引用(别名)
ref = 20; // 修改 ref 就是修改 num
cout << num; // 输出 20
📝 引用的基本使用
1. 定义引用
#include <iostream>
using namespace std;
int main() {
int age = 20;
int& ageRef = age; // ageRef 是 age 的引用
cout << "age = " << age << endl;
cout << "ageRef = " << ageRef << endl;
// 修改引用
ageRef = 30;
cout << "修改后:" << endl;
cout << "age = " << age << endl;
cout << "ageRef = " << ageRef << endl;
return 0;
}
输出:
age = 20
ageRef = 20
修改后:
age = 30
ageRef = 30
2. 引用的特点
int x = 10;
int& ref = x; // ✅ 正确:定义时必须初始化
int& ref2; // ❌ 错误:引用必须初始化
int y = 20;
ref = y; // ❌ 注意:这不是让ref引用y,而是把y的值赋给x
重要规则:
- ✅ 引用必须在定义时初始化
- ✅ 引用一旦绑定,不能改变引用对象
- ✅ 引用不能为 NULL
🔄 引用 vs 指针
| 特性 | 引用 | 指针 |
|---|---|---|
| 语法 | int& ref = num; | int* ptr = # |
| 初始化 | 必须初始化 | 可以不初始化 |
| 重新赋值 | 不能改变引用对象 | 可以指向其他变量 |
| 空值 | 不能为空 | 可以为 nullptr |
| 使用 | 直接使用 | 需要解引用 *ptr |
#include <iostream>
using namespace std;
int main() {
int a = 10, b = 20;
// 引用
int& ref = a;
ref = 30; // 修改 a 的值
cout << "a = " << a << endl; // 30
// 指针
int* ptr = &a;
*ptr = 40; // 修改 a 的值
cout << "a = " << a << endl; // 40
ptr = &b; // 指针可以改变指向
*ptr = 50; // 修改 b 的值
cout << "b = " << b << endl; // 50
return 0;
}
📤 引用作为函数参数
值传递 vs 引用传递
#include <iostream>
using namespace std;
// 值传递 - 不能修改原变量
void byValue(int x) {
x = 100;
}
// 引用传递 - 可以修改原变量
void byReference(int& x) {
x = 100;
}
int main() {
int num = 50;
cout << "原始值:" << num << endl;
byValue(num);
cout << "值传递后:" << num << endl;
byReference(num);
cout << "引用传递后:" << num << endl;
return 0;
}
输出:
原始值:50
值传递后:50
引用传递后:100
交换两个数(引用版)
#include <iostream>
using namespace std;
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 10, y = 20;
cout << "交换前:x = " << x << ", y = " << y << endl;
swap(x, y);
cout << "交换后:x = " << x << ", y = " << y << endl;
return 0;
}
输出:
交换前:x = 10, y = 20
交换后:x = 20, y = 10
避免大对象的复制
引用传递可以避免大对象的复制,提高效率:
#include <iostream>
#include <string>
using namespace std;
struct Student {
int id;
string name;
double scores[10];
};
// 值传递 - 会复制整个结构体(效率低)
void printStudent1(Student s) {
cout << s.id << " - " << s.name << endl;
}
// 引用传递 - 不复制,效率高
void printStudent2(const Student& s) {
cout << s.id << " - " << s.name << endl;
}
int main() {
Student stu = {1001, "张三", {90, 85, 88}};
printStudent1(stu); // 效率低
printStudent2(stu); // 效率高(推荐)
return 0;
}
const 引用
使用 const 引用可以防止函数修改参数,同时避免复制:
void print(const string& str) {
cout << str << endl;
// str = "xxx"; // ❌ 错误:不能修改const引用
}
💾 动态内存分配
为什么需要动态内存?
之前我们使用的数组大小都是固定的:
int arr[100]; // 固定大小,可能浪费或不够用
动态内存可以在运行时根据需要分配内存:
int n;
cin >> n;
int* arr = new int[n]; // 运行时决定大小
new 和 delete 运算符
分配单个变量
#include <iostream>
using namespace std;
int main() {
// 分配内存
int* ptr = new int;
*ptr = 100;
cout << "*ptr = " << *ptr << endl;
// 释放内存
delete ptr;
ptr = nullptr; // 好习惯:释放后置为nullptr
return 0;
}
分配数组
#include <iostream>
using namespace std;
int main() {
int n;
cout << "请输入数组大小:";
cin >> n;
// 动态分配数组
int* arr = new int[n];
// 使用数组
for (int i = 0; i < n; i++) {
arr[i] = i * 10;
}
cout << "数组内容:";
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
cout << endl;
// 释放数组内存
delete[] arr; // 注意:数组要用 delete[]
arr = nullptr;
return 0;
}
输出:
请输入数组大小:5
数组内容:0 10 20 30 40
内存泄漏
内存泄漏是指分配的内存没有被释放,导致内存浪费。
// ❌ 内存泄漏示例
void memoryLeak() {
int* ptr = new int[1000];
// 忘记 delete[] ptr;
} // ptr被销毁,但内存没有释放!
// ✅ 正确做法
void noLeak() {
int* ptr = new int[1000];
// ... 使用 ptr
delete[] ptr;
ptr = nullptr;
}
🌟 实战案例
案例1:动态数组输入输出
#include <iostream>
using namespace std;
int main() {
int n;
cout << "请输入学生人数:";
cin >> n;
// 动态分配数组
double* scores = new double[n];
// 输入成绩
cout << "请输入" << n << "个学生的成绩:" << endl;
for (int i = 0; i < n; i++) {
cout << "第" << (i + 1) << "个学生:";
cin >> scores[i];
}
// 计算平均分
double sum = 0;
for (int i = 0; i < n; i++) {
sum += scores[i];
}
double average = sum / n;
cout << "平均分:" << average << endl;
// 释放内存
delete[] scores;
return 0;
}
案例2:动态二维数组
#include <iostream>
using namespace std;
int main() {
int rows, cols;
cout << "请输入行数和列数:";
cin >> rows >> cols;
// 分配二维数组
int** matrix = new int*[rows];
for (int i = 0; i < rows; i++) {
matrix[i] = new int[cols];
}
// 初始化
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j;
}
}
// 打印
cout << "矩阵内容:" << endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << matrix[i][j] << "\t";
}
cout << endl;
}
// 释放内存
for (int i = 0; i < rows; i++) {
delete[] matrix[i];
}
delete[] matrix;
return 0;
}
案例3:返回动态分配的数组
#include <iostream>
using namespace std;
int* createArray(int size, int value) {
int* arr = new int[size];
for (int i = 0; i < size; i++) {
arr[i] = value;
}
return arr;
}
int main() {
int* myArray = createArray(5, 100);
cout << "数组内容:";
for (int i = 0; i < 5; i++) {
cout << myArray[i] << " ";
}
cout << endl;
delete[] myArray; // 记得释放
return 0;
}
案例4:引用返回值
#include <iostream>
using namespace std;
int& getElement(int* arr, int index) {
return arr[index];
}
int main() {
int arr[5] = {10, 20, 30, 40, 50};
cout << "原数组:";
for (int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
cout << endl;
// 通过引用修改数组元素
getElement(arr, 2) = 300;
cout << "修改后:";
for (int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
输出:
原数组:10 20 30 40 50
修改后:10 20 300 40 50
🎯 小结
| 概念 | 说明 | 示例 |
|---|---|---|
| 引用定义 | 变量的别名 | int& ref = num; |
| 引用特点 | 必须初始化,不能改变 | - |
| 引用传参 | 避免复制,可修改原值 | void func(int& x) |
| const引用 | 不能修改,但避免复制 | void func(const int& x) |
| new | 动态分配内存 | int* p = new int; |
| delete | 释放内存 | delete p; |
| new[] | 动态分配数组 | int* arr = new int[n]; |
| delete[] | 释放数组内存 | delete[] arr; |
重要提醒:
- ⚠️ 引用必须初始化
- ⚠️ new 必须配对 delete
- ⚠️ new[] 必须配对 delete[]
- ⚠️ 避免内存泄漏
- ⚠️ delete 后将指针置为 nullptr
💪 练习题
- 写一个函数,使用引用交换两个字符串
- 写一个函数,接收数组引用,返回最大值和最小值(使用引用参数)
- 动态分配一个数组,输入n个整数,找出其中的最大值
- 实现一个动态字符串复制函数
- 创建一个动态分配的学生数组,实现添加、删除、显示功能
点击查看参考答案
题目1 参考答案:
#include <iostream>
#include <string>
using namespace std;
void swapStrings(string& a, string& b) {
string temp = a;
a = b;
b = temp;
}
int main() {
string s1 = "Hello";
string s2 = "World";
cout << "交换前:" << s1 << ", " << s2 << endl;
swapStrings(s1, s2);
cout << "交换后:" << s1 << ", " << s2 << endl;
return 0;
}
题目3 参考答案:
#include <iostream>
using namespace std;
int main() {
int n;
cout << "请输入数组大小:";
cin >> n;
int* arr = new int[n];
cout << "请输入" << n << "个整数:" << endl;
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
int maxVal = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] > maxVal) {
maxVal = arr[i];
}
}
cout << "最大值:" << maxVal << endl;
delete[] arr;
return 0;
}
🚀 下一步
太棒了!你已经掌握了引用和动态内存管理!
下一章,我们要学习结构体和枚举,这是组织复杂数据的重要工具!
