C++ 从入门到精通C++ 从入门到精通
首页
基础教程
进阶教程
实战项目
编程指南
首页
基础教程
进阶教程
实战项目
编程指南
  • 💪 进阶基础

    • 📚 C++ 进阶教程
    • 第8章 - 指针基础
    • 第9章 - 引用和动态内存
    • 第10章 - 结构体和枚举
    • 第11章 - 文件操作
    • 第12章 - 预处理器和命名空间
  • 🎯 面向对象编程

    • 第13章 - 类和对象基础
    • 第14章 - 构造函数和析构函数
    • 第15章 - 封装、继承、多态
    • 第16章 - 运算符重载
    • 第17章 - 模板基础
    • 第18章 - 异常处理
  • 📦 STL 标准模板库

    • 第19章 - Vector 和 String
    • 第20章 - 容器类
    • 第21章 - Map 和 Set
    • 第22章 - 算法和迭代器

第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 = &num;
初始化必须初始化可以不初始化
重新赋值不能改变引用对象可以指向其他变量
空值不能为空可以为 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

💪 练习题

  1. 写一个函数,使用引用交换两个字符串
  2. 写一个函数,接收数组引用,返回最大值和最小值(使用引用参数)
  3. 动态分配一个数组,输入n个整数,找出其中的最大值
  4. 实现一个动态字符串复制函数
  5. 创建一个动态分配的学生数组,实现添加、删除、显示功能
点击查看参考答案

题目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;
}

🚀 下一步

太棒了!你已经掌握了引用和动态内存管理!

下一章,我们要学习结构体和枚举,这是组织复杂数据的重要工具!

➡️ 第10章 - 结构体和枚举

最近更新: 2025/12/26 17:25
Contributors: 王长安
Prev
第8章 - 指针基础
Next
第10章 - 结构体和枚举