C++从入门到精通教程

教程简介

零基础C++编程完整教程,涵盖基本语法、面向对象编程、模板编程、STL标准库、智能指针、异常处理、文件操作等核心概念,配有图书管理系统实战项目,适合编程初学者系统学习C++。

C++ 从入门到精通教程

本教程面向零基础学习者,从最基础的概念讲起,逐步深入到面向对象编程、模板、STL标准库、智能指针等高级主题,最终通过一个实战项目巩固所学知识。


目录

  1. C++ 简介与历史
  2. 开发环境搭建
  3. 第一个C++程序
  4. 基本语法
  5. 函数
  6. 数组与字符串
  7. 指针与引用
  8. 面向对象编程基础——类与对象
  9. 继承
  10. 多态
  11. 封装
  12. 模板编程
  13. STL标准模板库
  14. 智能指针
  15. 异常处理
  16. 文件操作
  17. 实战项目:图书管理系统
  18. 学习建议与进阶路线

1. C++ 简介与历史

什么是C++?

C++ 是一门通用的、编译型的编程语言,由 Bjarne Stroustup 于1979年在贝尔实验室开始开发。它最初被称为 "C with Classes"(带类的C),在C语言的基础上增加了面向对象的特性。

C++ 的发展历史

年份 里程碑
1979 Bjarne Stroustrup 开始开发 "C with Classes"
1983 正式命名为 C++
1985 发布第一个商业版本,出版《The C++ Programming Language》
1998 C++98 标准发布(ISO/IEC 14882:1998)
2003 C++03 标准(修复版)
2011 C++11 —— 重大更新,引入自动类型推导、智能指针、Lambda、右值引用等
2014 C14 —— C11 的小幅改进
2017 C++17 —— 引入 std::optional、结构化绑定、文件系统库等
2020 C++20 —— 引入概念(Concepts)、协程、范围(Ranges)、模块(Modules)
2023 C++23 —— 进一步完善标准库

为什么学 C++?

  • 性能极高:接近硬件层的控制能力,广泛用于游戏引擎、操作系统、嵌入式系统
  • 应用广泛:操作系统(Windows/Linux内核部分)、游戏(Unreal Engine)、数据库(MySQL)、浏览器(Chrome)等
  • 理解底层:学习C++能帮助你深入理解计算机是如何工作的
  • 就业面广:系统开发、游戏开发、金融量化、嵌入式等领域都需要C++

2. 开发环境搭建

方式一:本地安装(推荐)

Windows

  1. 安装编译器:下载并安装 MinGW-w64 或直接安装 Visual Studio(社区版免费)
  2. 安装编辑器:推荐 Visual Studio Code + C/C++ 扩展
  3. 验证安装:打开命令行,输入 g++ --version

macOS

# 安装 Xcode Command Line Tools(自带 clang 编译器)
xcode-select --install

Linux (Ubuntu/Debian)

sudo apt update
sudo apt install g++ build-essential

方式二:在线编译器

如果不想本地安装,可以使用在线编译器快速体验:

编译与运行

# 编译
g++ -o hello hello.cpp

# 运行
./hello

3. 第一个C++程序

#include <iostream>  // 引入输入输出流库

int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

逐行解析:

  • #include <iostream>:预处理指令,告诉编译器引入 iostream 库(提供输入输出功能)
  • int main():程序的入口函数,每个C++程序必须有且只有一个 main 函数
  • std::cout:标准输出流对象,<< 是输出运算符
  • std::endl:换行并刷新缓冲区
  • return 0:向操作系统返回 0,表示程序正常结束

简化写法(使用命名空间):

#include <iostream>
using namespace std;  // 引入 std 命名空间

int main() {
    cout << "Hello, World!" << endl;
    return 0;
}

建议:初学时可以用 using namespace std;,但在实际项目中建议使用 std:: 前缀,避免命名冲突。


4. 基本语法

4.1 变量与数据类型

变量是用来存储数据的容器。C++ 是静态类型语言,每个变量在使用前必须声明类型。

#include <iostream>
using namespace std;

int main() {
    // 整数类型
    int age = 25;              // 通常 4 字节,范围约 ±21亿
    long long bigNum = 10000000000LL;  // 8 字节
    short smallNum = 100;      // 2 字节

    // 浮点类型
    float price = 9.99f;       // 4 字节,约 6-7 位有效数字
    double pi = 3.14159265358979;  // 8 字节,约 15-16 位有效数字

    // 字符类型
    char grade = 'A';          // 1 字节
    char newline = '\n';       // 转义字符:换行

    // 布尔类型
    bool isStudent = true;     // 1 字节,true 或 false

    // 字符串类型(C++ 标准库提供)
    string name = "张三";      // 需要 #include <string>

    // 输出
    cout << "姓名: " << name << endl;
    cout << "年龄: " << age << endl;
    cout << "成绩: " << grade << endl;
    cout << "是否学生: " << isStudent << endl;

    return 0;
}

4.2 常量

const double PI = 3.14159265358979;  // const 修饰,不可修改
constexpr int MAX_SIZE = 100;        // 编译期常量(C++11)

4.3 运算符

#include <iostream>
using namespace std;

int main() {
    int a = 10, b = 3;

    // 算术运算符
    cout << "a + b = " << a + b << endl;   // 13
    cout << "a - b = " << a - b << endl;   // 7
    cout << "a * b = " << a * b << endl;   // 30
    cout << "a / b = " << a / b << endl;   // 3(整数除法,截断小数)
    cout << "a % b = " << a % b << endl;   // 1(取余)

    // 比较运算符(返回 bool)
    cout << "a > b: " << (a > b) << endl;   // 1 (true)
    cout << "a == b: " << (a == b) << endl; // 0 (false)

    // 逻辑运算符
    bool x = true, y = false;
    cout << "x && y: " << (x && y) << endl; // 0(与)
    cout << "x || y: " << (x || y) << endl; // 1(或)
    cout << "!x: " << (!x) << endl;          // 0(非)

    // 自增自减
    int c = 5;
    cout << "c++ = " << c++ << endl;  // 先使用再自增,输出 5,之后 c=6
    cout << "++c = " << ++c << endl;  // 先自增再使用,c=7,输出 7

    return 0;
}

4.4 条件语句

#include <iostream>
using namespace std;

int main() {
    int score;
    cout << "请输入成绩: ";
    cin >> score;

    // if-else if-else
    if (score >= 90) {
        cout << "优秀" << endl;
    } else if (score >= 80) {
        cout << "良好" << endl;
    } else if (score >= 60) {
        cout << "及格" << endl;
    } else {
        cout << "不及格" << endl;
    }

    // switch 语句
    int day = 3;
    switch (day) {
        case 1: cout << "周一" << endl; break;
        case 2: cout << "周二" << endl; break;
        case 3: cout << "周三" << endl; break;
        case 4: cout << "周四" << endl; break;
        case 5: cout << "周五" << endl; break;
        default: cout << "周末" << endl; break;
    }

    // 三元运算符
    int a = 10, b = 20;
    int maxVal = (a > b) ? a : b;
    cout << "较大值: " << maxVal << endl;

    return 0;
}

4.5 循环语句

#include <iostream>
using namespace std;

int main() {
    // for 循环
    cout << "=== for 循环 ===" << endl;
    for (int i = 1; i <= 5; i++) {
        cout << "第 " << i << " 次" << endl;
    }

    // while 循环
    cout << "=== while 循环 ===" << endl;
    int count = 0;
    while (count < 3) {
        cout << "count = " << count << endl;
        count++;
    }

    // do-while 循环(至少执行一次)
    cout << "=== do-while 循环 ===" << endl;
    int num = 10;
    do {
        cout << "num = " << num << endl;
        num--;
    } while (num > 7);

    // 范围 for 循环(C++11)
    cout << "=== 范围 for ===" << endl;
    int arr[] = {10, 20, 30, 40, 50};
    for (int val : arr) {
        cout << val << " ";
    }
    cout << endl;

    // break 与 continue
    cout << "=== break 示例 ===" << endl;
    for (int i = 0; i < 10; i++) {
        if (i == 5) break;  // 遇到 5 就跳出循环
        cout << i << " ";
    }
    cout << endl;

    cout << "=== continue 示例 ===" << endl;
    for (int i = 0; i < 10; i++) {
        if (i % 2 == 0) continue;  // 跳过偶数
        cout << i << " ";
    }
    cout << endl;

    return 0;
}

4.6 输入输出

#include <iostream>
#include <string>
using namespace std;

int main() {
    // 基本输入
    int age;
    cout << "请输入你的年龄: ";
    cin >> age;
    cout << "你 " << age << " 岁了" << endl;

    // 字符串输入
    string name;
    cout << "请输入你的名字: ";
    cin >> name;  // 只能读取一个单词
    cout << "你好, " << name << "!" << endl;

    // 读取整行
    string sentence;
    cout << "请输入一句话: ";
    cin.ignore();  // 清除之前的换行符
    getline(cin, sentence);
    cout << "你说的是: " << sentence << endl;

    // 格式化输出
    double value = 3.14159;
    cout << "默认: " << value << endl;
    cout.precision(2);
    cout << "保留2位: " << fixed << value << endl;

    return 0;
}

5. 函数

函数是组织代码的基本单元,它将一段可复用的逻辑封装起来。

5.1 函数基础

#include <iostream>
using namespace std;

// 函数声明(原型)
int add(int a, int b);
void greet(string name);
double calculateArea(double radius);

int main() {
    int result = add(3, 5);
    cout << "3 + 5 = " << result << endl;

    greet("小明");

    cout << "半径为5的圆面积: " << calculateArea(5.0) << endl;

    return 0;
}

// 函数定义
int add(int a, int b) {
    return a + b;
}

void greet(string name) {
    cout << "你好, " << name << "!" << endl;
}

double calculateArea(double radius) {
    const double PI = 3.14159265358979;
    return PI * radius * radius;
}

5.2 函数重载

C++ 允许同名函数有不同的参数列表(参数类型或数量不同):

#include <iostream>
using namespace std;

// 重载的 print 函数
void print(int x) {
    cout << "整数: " << x << endl;
}

void print(double x) {
    cout << "浮点数: " << x << endl;
}

void print(string x) {
    cout << "字符串: " << x << endl;
}

int main() {
    print(42);
    print(3.14);
    print("Hello");
    return 0;
}

5.3 默认参数与内联函数

#include <iostream>
using namespace std;

// 默认参数(从右往左设置)
void printInfo(string name, int age = 18, string city = "北京") {
    cout << name << ", " << age << "岁, " << city << endl;
}

// 内联函数(建议编译器将函数体展开,减少调用开销)
inline int square(int x) {
    return x * x;
}

int main() {
    printInfo("张三");                // 张三, 18岁, 北京
    printInfo("李四", 25);            // 李四, 25岁, 北京
    printInfo("王五", 30, "上海");     // 王五, 30岁, 上海

    cout << "5 的平方: " << square(5) << endl;

    return 0;
}

5.4 Lambda 表达式(C++11)

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int main() {
    // 基本 lambda
    auto add = [](int a, int b) -> int {
        return a + b;
    };
    cout << "3 + 4 = " << add(3, 4) << endl;

    // 带捕获的 lambda
    int factor = 10;
    auto multiply = [factor](int x) { return x * factor; };
    cout << "5 * 10 = " << multiply(5) << endl;

    // 在算法中使用 lambda
    vector<int> nums = {5, 2, 8, 1, 9, 3};
    sort(nums.begin(), nums.end(), [](int a, int b) {
        return a > b;  // 降序排序
    });

    for (int n : nums) cout << n << " ";
    cout << endl;  // 输出: 9 8 5 3 2 1

    return 0;
}

6. 数组与字符串

6.1 数组

#include <iostream>
using namespace std;

int main() {
    // 声明并初始化数组
    int scores[5] = {90, 85, 78, 92, 88};

    // 访问和修改
    cout << "第一个成绩: " << scores[0] << endl;
    scores[2] = 80;  // 修改第三个元素

    // 遍历数组
    cout << "所有成绩: ";
    for (int i = 0; i < 5; i++) {
        cout << scores[i] << " ";
    }
    cout << endl;

    // 计算平均值
    double sum = 0;
    for (int score : scores) {
        sum += score;
    }
    cout << "平均分: " << sum / 5 << endl;

    // 二维数组
    int matrix[3][3] = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };

    cout << "矩阵:" << endl;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            cout << matrix[i][j] << " ";
        }
        cout << endl;
    }

    return 0;
}

6.2 字符串

#include <iostream>
#include <string>
using namespace std;

int main() {
    // C++ string 类(推荐使用)
    string s1 = "Hello";
    string s2 = "World";

    // 拼接
    string s3 = s1 + " " + s2;
    cout << s3 << endl;           // Hello World

    // 长度
    cout << "长度: " << s3.length() << endl;

    // 访问单个字符
    cout << "第一个字符: " << s3[0] << endl;

    // 查找
    size_t pos = s3.find("World");
    if (pos != string::npos) {
        cout << "找到 'World' 在位置: " << pos << endl;
    }

    // 子串
    string sub = s3.substr(0, 5);
    cout << "子串: " << sub << endl;  // Hello

    // 比较
    if (s1 == "Hello") {
        cout << "字符串相等" << endl;
    }

    // 遍历字符串
    for (char c : s3) {
        cout << c << " ";
    }
    cout << endl;

    return 0;
}

7. 指针与引用

指针是 C++ 最强大也最容易出错的特性之一。理解指针是深入学习 C++ 的关键。

7.1 指针基础

#include <iostream>
using namespace std;

int main() {
    int num = 42;
    int* ptr = &num;  // ptr 存储 num 的内存地址

    cout << "num 的值: " << num << endl;         // 42
    cout << "num 的地址: " << &num << endl;       // 0x7fff...
    cout << "ptr 的值: " << ptr << endl;          // 同上,存储的是地址
    cout << "ptr 指向的值: " << *ptr << endl;     // 42(解引用)

    // 通过指针修改值
    *ptr = 100;
    cout << "修改后 num = " << num << endl;      // 100

    // 空指针
    int* nullPtr = nullptr;  // C++11 推荐用 nullptr 代替 NULL
    if (nullPtr == nullptr) {
        cout << "这是一个空指针" << endl;
    }

    return 0;
}

7.2 动态内存分配

#include <iostream>
using namespace std;

int main() {
    // 动态分配单个变量
    int* p = new int(42);
    cout << *p << endl;  // 42
    delete p;            // 释放内存
    p = nullptr;

    // 动态分配数组
    int n = 5;
    int* arr = new int[n];
    for (int i = 0; i < n; i++) {
        arr[i] = i * 10;
    }
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    delete[] arr;  // 释放数组内存
    arr = nullptr;

    return 0;
}

7.3 引用

引用是变量的别名,必须在声明时初始化,且不能重新绑定:

#include <iostream>
using namespace std;

// 引用作为函数参数(传递引用,避免拷贝)
void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

// 引用作为返回值(避免拷贝大型对象)
int& getElement(int arr[], int index) {
    return arr[index];
}

int main() {
    int x = 10, y = 20;
    cout << "交换前: x=" << x << ", y=" << y << endl;

    swap(x, y);
    cout << "交换后: x=" << x << ", y=" << y << endl;

    // 引用即别名
    int num = 42;
    int& ref = num;  // ref 是 num 的别名
    ref = 100;
    cout << "num = " << num << endl;  // 100

    return 0;
}

8. 面向对象编程基础——类与对象

面向对象编程(OOP)是 C++ 的核心特性之一。类是对象的蓝图,对象是类的实例。

8.1 定义类

#include <iostream>
#include <string>
using namespace std;

class Student {
private:    // 私有成员,外部不可直接访问
    string name;
    int age;
    double score;

public:     // 公有成员,外部可以访问
    // 构造函数(创建对象时自动调用)
    Student(string n, int a, double s) : name(n), age(a), score(s) {}

    // 默认构造函数
    Student() : name("未知"), age(0), score(0.0) {}

    // 析构函数(对象销毁时自动调用)
    ~Student() {
        // 在这里释放资源(如果有动态内存分配)
    }

    // 成员函数(方法)
    void display() const {
        cout << "姓名: " << name
             << ", 年龄: " << age
             << ", 成绩: " << score << endl;
    }

    // Getter 方法
    string getName() const { return name; }
    int getAge() const { return age; }
    double getScore() const { return score; }

    // Setter 方法
    void setName(const string& n) { name = n; }
    void setAge(int a) { age = a; }
    void setScore(double s) { score = s; }

    // 判断是否及格
    bool isPassed() const {
        return score >= 60;
    }
};

int main() {
    // 创建对象
    Student stu1("张三", 20, 85.5);
    stu1.display();

    Student stu2;  // 使用默认构造函数
    stu2.setName("李四");
    stu2.setAge(21);
    stu2.setScore(55);
    stu2.display();

    cout << stu1.getName() << " 是否及格: "
         << (stu1.isPassed() ? "是" : "否") << endl;
    cout << stu2.getName() << " 是否及格: "
         << (stu2.isPassed() ? "是" : "否") << endl;

    return 0;
}

8.2 构造函数详解

#include <iostream>
using namespace std;

class Rectangle {
private:
    double width;
    double height;

public:
    // 1. 默认构造函数
    Rectangle() : width(0), height(0) {
        cout << "默认构造函数被调用" << endl;
    }

    // 2. 参数化构造函数
    Rectangle(double w, double h) : width(w), height(h) {
        cout << "参数化构造函数被调用" << endl;
    }

    // 3. 拷贝构造函数
    Rectangle(const Rectangle& other)
        : width(other.width), height(other.height) {
        cout << "拷贝构造函数被调用" << endl;
    }

    double area() const { return width * height; }
    double perimeter() const { return 2 * (width + height); }

    void display() const {
        cout << "矩形: " << width << " x " << height
             << ", 面积=" << area()
             << ", 周长=" << perimeter() << endl;
    }
};

int main() {
    Rectangle r1;              // 默认构造
    Rectangle r2(3.0, 4.0);    // 参数化构造
    Rectangle r3 = r2;         // 拷贝构造

    r1.display();
    r2.display();
    r3.display();

    return 0;
}

8.3 this 指针与运算符重载

#include <iostream>
using namespace std;

class Vector2D {
private:
    double x, y;

public:
    Vector2D(double x = 0, double y = 0) : x(x), y(y) {}

    // 运算符重载:向量加法
    Vector2D operator+(const Vector2D& other) const {
        return Vector2D(x + other.x, y + other.y);
    }

    // 运算符重载:向量减法
    Vector2D operator-(const Vector2D& other) const {
        return Vector2D(x - other.x, y - other.y);
    }

    // 运算符重载:标量乘法
    Vector2D operator*(double scalar) const {
        return Vector2D(x * scalar, y * scalar);
    }

    // 运算符重载:输出流
    friend ostream& operator<<(ostream& os, const Vector2D& v) {
        os << "(" << v.x << ", " << v.y << ")";
        return os;
    }

    // 运算符重载:比较
    bool operator==(const Vector2D& other) const {
        return x == other.x && y == other.y;
    }

    // 计算长度
    double magnitude() const {
        return sqrt(x * x + y * y);
    }
};

int main() {
    Vector2D v1(3, 4);
    Vector2D v2(1, 2);

    cout << "v1 = " << v1 << endl;
    cout << "v2 = " << v2 << endl;
    cout << "v1 + v2 = " << v1 + v2 << endl;
    cout << "v1 - v2 = " << v1 - v2 << endl;
    cout << "v1 * 2 = " << v1 * 2 << endl;
    cout << "|v1| = " << v1.magnitude() << endl;

    return 0;
}

9. 继承

继承允许我们基于已有的类创建新类,实现代码复用。

9.1 基本继承

#include <iostream>
#include <string>
using namespace std;

// 基类
class Shape {
protected:  // protected 成员可以被子类访问
    string color;

public:
    Shape(string c) : color(c) {}
    virtual ~Shape() {}  // 虚析构函数(重要!)

    string getColor() const { return color; }

    // 虚函数,子类可以重写
    virtual double area() const = 0;        // 纯虚函数,使 Shape 成为抽象类
    virtual void display() const {
        cout << "颜色: " << color;
    }
};

// 派生类:圆形
class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r, string c) : Shape(c), radius(r) {}

    double area() const override {
        return 3.14159265358979 * radius * radius;
    }

    void display() const override {
        Shape::display();
        cout << ", 圆形, 半径=" << radius
             << ", 面积=" << area() << endl;
    }
};

// 派生类:矩形
class Rectangle : public Shape {
private:
    double width, height;

public:
    Rectangle(double w, double h, string c) : Shape(c), width(w), height(h) {}

    double area() const override {
        return width * height;
    }

    void display() const override {
        Shape::display();
        cout << ", 矩形, " << width << "x" << height
             << ", 面积=" << area() << endl;
    }
};

int main() {
    Circle c(5.0, "红色");
    Rectangle r(4.0, 6.0, "蓝色");

    c.display();
    r.display();

    // 多态使用
    Shape* shapes[] = {&c, &r};
    for (const Shape* s : shapes) {
        cout << "面积: " << s->area() << endl;
    }

    return 0;
}

9.2 继承方式

class Base {
public:
    int pub;
protected:
    int prot;
private:
    int priv;
};

// public 继承:保持原有访问权限
class PublicDerived : public Base {
    // pub → public, prot → protected, priv → 不可访问
};

// protected 继承:public 和 protected 都变为 protected
class ProtectedDerived : protected Base {
    // pub → protected, prot → protected, priv → 不可访问
};

// private 继承:全部变为 private(默认继承方式)
class PrivateDerived : private Base {
    // pub → private, prot → private, priv → 不可访问
};

9.3 多重继承

#include <iostream>
using namespace std;

class Flyable {
public:
    virtual void fly() { cout << "飞行中..." << endl; }
};

class Swimmable {
public:
    virtual void swim() { cout << "游泳中..." << endl; }
};

// 多重继承
class Duck : public Flyable, public Swimmable {
public:
    void fly() override { cout << "鸭子在飞!" << endl; }
    void swim() override { cout << "鸭子在游泳!" << endl; }
};

int main() {
    Duck duck;
    duck.fly();
    duck.swim();
    return 0;
}

10. 多态

多态是 OOP 的核心概念之一,允许用统一的接口处理不同类型的对象。

10.1 虚函数与运行时多态

#include <iostream>
#include <vector>
#include <memory>
using namespace std;

class Animal {
public:
    virtual void speak() const = 0;
    virtual string type() const = 0;
    virtual ~Animal() {}
};

class Dog : public Animal {
public:
    void speak() const override { cout << "汪汪!" << endl; }
    string type() const override { return "狗"; }
};

class Cat : public Animal {
public:
    void speak() const override { cout << "喵喵!" << endl; }
    string type() const override { return "猫"; }
};

class Bird : public Animal {
public:
    void speak() const override { cout << "叽叽!" << endl; }
    string type() const override { return "鸟"; }
};

void makeSound(const Animal& animal) {
    cout << animal.type() << " 说: ";
    animal.speak();
}

int main() {
    Dog dog;
    Cat cat;
    Bird bird;

    makeSound(dog);   // 狗 说: 汪汪!
    makeSound(cat);   // 猫 说: 喵喵!
    makeSound(bird);  // 鸟 说: 叽叽!

    // 使用容器存储不同类型的对象
    vector<unique_ptr<Animal>> zoo;
    zoo.push_back(make_unique<Dog>());
    zoo.push_back(make_unique<Cat>());
    zoo.push_back(make_unique<Bird>());

    cout << "\n--- 动物园 ---" << endl;
    for (const auto& animal : zoo) {
        makeSound(*animal);
    }

    return 0;
}

10.2 抽象类与接口

#include <iostream>
using namespace std;

// 接口类(纯虚函数)
class Printable {
public:
    virtual void print() const = 0;
    virtual ~Printable() {}
};

class Serializable {
public:
    virtual string serialize() const = 0;
    virtual ~Serializable() {}
};

// 实现多个接口
class Document : public Printable, public Serializable {
private:
    string title;
    string content;

public:
    Document(string t, string c) : title(t), content(c) {}

    void print() const override {
        cout << "=== " << title << " ===" << endl;
        cout << content << endl;
    }

    string serialize() const override {
        return "{\"title\":\"" + title + "\",\"content\":\"" + content + "\"}";
    }
};

int main() {
    Document doc("报告", "这是一份重要的报告。");
    doc.print();
    cout << "序列化: " << doc.serialize() << endl;
    return 0;
}

11. 封装

封装是将数据和操作数据的方法绑定在一起,并隐藏内部实现细节。

11.1 访问控制

#include <iostream>
using namespace std;

class BankAccount {
private:    // 只有类内部可以访问
    string owner;
    double balance;
    static int totalAccounts;  // 静态成员,所有对象共享

public:     // 外部可以访问
    // 构造函数
    BankAccount(string name, double initial)
        : owner(name), balance(initial) {
        totalAccounts++;
    }

    // 析构函数
    ~BankAccount() {
        totalAccounts--;
    }

    // 公共接口
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            cout << "存入 " << amount << " 元,余额: " << balance << " 元" << endl;
        } else {
            cout << "存款金额必须大于0" << endl;
        }
    }

    bool withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            cout << "取出 " << amount << " 元,余额: " << balance << " 元" << endl;
            return true;
        }
        cout << "取款失败!余额不足或金额无效" << endl;
        return false;
    }

    double getBalance() const { return balance; }
    string getOwner() const { return owner; }

    static int getTotalAccounts() { return totalAccounts; }
};

// 静态成员初始化
int BankAccount::totalAccounts = 0;

int main() {
    BankAccount acc1("张三", 1000);
    BankAccount acc2("李四", 500);

    acc1.deposit(500);
    acc1.withdraw(200);
    acc1.withdraw(2000);  // 余额不足

    cout << "\n当前账户总数: " << BankAccount::getTotalAccounts() << endl;

    return 0;
}

12. 模板编程

模板是 C++ 泛型编程的基础,允许编写与类型无关的代码。

12.1 函数模板

#include <iostream>
#include <string>
using namespace std;

// 函数模板
template <typename T>
T maxValue(T a, T b) {
    return (a > b) ? a : b;
}

// 多类型参数的模板
template <typename T, typename U>
void printPair(T first, U second) {
    cout << "(" << first << ", " << second << ")" << endl;
}

// 模板特化
template <>
string maxValue(string a, string b) {
    return (a.length() > b.length()) ? a : b;
}

int main() {
    cout << maxValue(10, 20) << endl;       // 20
    cout << maxValue(3.14, 2.71) << endl;   // 3.14
    cout << maxValue(string("Hello"), string("Hi")) << endl;  // Hello(长度比较)

    printPair("年龄", 25);
    printPair(3.14, "是圆周率");

    return 0;
}

12.2 类模板

#include <iostream>
#include <stdexcept>
using namespace std;

template <typename T>
class Stack {
private:
    static const int MAX_SIZE = 100;
    T data[MAX_SIZE];
    int top;

public:
    Stack() : top(-1) {}

    void push(const T& item) {
        if (top >= MAX_SIZE - 1) {
            throw overflow_error("栈溢出");
        }
        data[++top] = item;
    }

    T pop() {
        if (isEmpty()) {
            throw underflow_error("栈为空");
        }
        return data[top--];
    }

    T peek() const {
        if (isEmpty()) {
            throw underflow_error("栈为空");
        }
        return data[top];
    }

    bool isEmpty() const { return top == -1; }
    int size() const { return top + 1; }
};

int main() {
    // 整数栈
    Stack<int> intStack;
    intStack.push(10);
    intStack.push(20);
    intStack.push(30);

    cout << "栈顶: " << intStack.peek() << endl;  // 30
    cout << "弹出: " << intStack.pop() << endl;    // 30
    cout << "弹出: " << intStack.pop() << endl;    // 20

    // 字符串栈
    Stack<string> strStack;
    strStack.push("Hello");
    strStack.push("World");

    cout << "弹出: " << strStack.pop() << endl;    // World

    return 0;
}

13. 标准模板库(STL)

STL 是 C++ 标准库的核心组成部分,提供了丰富的容器、算法和迭代器。

13.1 顺序容器

vector(动态数组)

#include <iostream>
#include <vector>
using namespace std;

int main() {
    // 创建与初始化
    vector<int> v1;                    // 空 vector
    vector<int> v2(5, 10);            // 5 个元素,每个都是 10
    vector<int> v3 = {1, 2, 3, 4, 5}; // 初始化列表

    // 添加元素
    v1.push_back(100);
    v1.push_back(200);
    v1.push_back(300);

    // 访问元素
    cout << "v1[0] = " << v1[0] << endl;
    cout << "v1.at(1) = " << v1.at(1) << endl;  // at() 会做边界检查
    cout << "v1.front() = " << v1.front() << endl;
    cout << "v1.back() = " << v1.back() << endl;

    // 容量相关
    cout << "大小: " << v1.size() << endl;
    cout << "容量: " << v1.capacity() << endl;

    // 遍历
    cout << "遍历 v3: ";
    for (int x : v3) cout << x << " ";
    cout << endl;

    // 使用迭代器遍历
    cout << "迭代器遍历 v1: ";
    for (auto it = v1.begin(); it != v1.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    // 插入和删除
    v3.insert(v3.begin() + 2, 99);  // 在索引2处插入99
    v3.erase(v3.begin());            // 删除第一个元素

    cout << "修改后的 v3: ";
    for (int x : v3) cout << x << " ";
    cout << endl;

    return 0;
}

list(双向链表)

#include <iostream>
#include <list>
using namespace std;

int main() {
    list<int> lst = {3, 1, 4, 1, 5, 9};

    // 添加元素
    lst.push_front(0);   // 头部添加
    lst.push_back(10);   // 尾部添加

    // 排序
    lst.sort();
    cout << "排序后: ";
    for (int x : lst) cout << x << " ";
    cout << endl;

    // 去重(需要先排序)
    lst.unique();
    cout << "去重后: ";
    for (int x : lst) cout << x << " ";
    cout << endl;

    // 反转
    lst.reverse();
    cout << "反转后: ";
    for (int x : lst) cout << x << " ";
    cout << endl;

    return 0;
}

deque(双端队列)

#include <iostream>
#include <deque>
using namespace std;

int main() {
    deque<int> dq = {2, 3, 4};

    dq.push_front(1);  // 头部插入
    dq.push_back(5);   // 尾部插入

    cout << "deque: ";
    for (int x : dq) cout << x << " ";
    cout << endl;

    dq.pop_front();
    dq.pop_back();
    cout << "弹出首尾后: ";
    for (int x : dq) cout << x << " ";
    cout << endl;

    return 0;
}

13.2 关联容器

map(键值对映射)

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    map<string, int> scores;

    // 插入元素
    scores["张三"] = 85;
    scores["李四"] = 92;
    scores["王五"] = 78;
    scores.insert({"赵六", 88});

    // 访问元素
    cout << "张三的成绩: " << scores["张三"] << endl;

    // 遍历
    cout << "所有成绩:" << endl;
    for (const auto& pair : scores) {
        cout << "  " << pair.first << ": " << pair.second << endl;
    }

    // 查找
    auto it = scores.find("李四");
    if (it != scores.end()) {
        cout << "找到李四: " << it->second << endl;
    }

    // 统计大小
    cout << "总人数: " << scores.size() << endl;

    // 删除
    scores.erase("王五");

    // 检查是否存在
    if (scores.count("王五") == 0) {
        cout << "王五已被删除" << endl;
    }

    return 0;
}

set(集合)

#include <iostream>
#include <set>
using namespace std;

int main() {
    set<int> s = {5, 3, 8, 1, 3, 5};  // 自动去重和排序

    s.insert(10);
    s.insert(2);

    cout << "set 元素(自动排序): ";
    for (int x : s) cout << x << " ";
    cout << endl;

    // 查找
    if (s.find(5) != s.end()) {
        cout << "找到 5" << endl;
    }

    cout << "集合大小: " << s.size() << endl;

    return 0;
}

13.3 常用算法

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;

int main() {
    vector<int> nums = {5, 2, 8, 1, 9, 3, 7, 4, 6};

    // 排序
    sort(nums.begin(), nums.end());
    cout << "升序: ";
    for (int x : nums) cout << x << " ";
    cout << endl;

    sort(nums.begin(), nums.end(), greater<int>());
    cout << "降序: ";
    for (int x : nums) cout << x << " ";
    cout << endl;

    // 查找
    auto it = find(nums.begin(), nums.end(), 8);
    if (it != nums.end()) {
        cout << "找到 8,位置: " << distance(nums.begin(), it) << endl;
    }

    // 二分查找(需要已排序)
    sort(nums.begin(), nums.end());
    bool found = binary_search(nums.begin(), nums.end(), 5);
    cout << "二分查找 5: " << (found ? "找到" : "未找到") << endl;

    // 计数
    int cnt = count_if(nums.begin(), nums.end(), [](int x) { return x > 5; });
    cout << "大于5的元素个数: " << cnt << endl;

    // 累加
    int sum = accumulate(nums.begin(), nums.end(), 0);
    cout << "总和: " << sum << endl;

    // 最大最小值
    auto [minIt, maxIt] = minmax_element(nums.begin(), nums.end());
    cout << "最小值: " << *minIt << ", 最大值: " << *maxIt << endl;

    // 反转
    reverse(nums.begin(), nums.end());
    cout << "反转后: ";
    for (int x : nums) cout << x << " ";
    cout << endl;

    // 去重(需要先排序)
    sort(nums.begin(), nums.end());
    auto last = unique(nums.begin(), nums.end());
    nums.erase(last, nums.end());
    cout << "去重后: ";
    for (int x : nums) cout << x << " ";
    cout << endl;

    return 0;
}

13.4 迭代器

#include <iostream>
#include <vector>
#include <list>
using namespace std;

int main() {
    vector<int> vec = {10, 20, 30, 40, 50};

    // 正向迭代器
    cout << "正向遍历: ";
    for (vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    // auto 简化
    cout << "反向遍历: ";
    for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    // const 迭代器(只读)
    cout << "只读遍历: ";
    for (auto it = vec.cbegin(); it != vec.cend(); ++it) {
        cout << *it << " ";
        // *it = 100;  // 错误!const 迭代器不能修改
    }
    cout << endl;

    // 迭代器算术
    auto it = vec.begin();
    advance(it, 2);  // 前进2步
    cout << "第3个元素: " << *it << endl;

    return 0;
}

14. 智能指针

C++11 引入的智能指针自动管理动态内存,避免内存泄漏。

14.1 unique_ptr(独占所有权)

#include <iostream>
#include <memory>
using namespace std;

class Resource {
    string name;
public:
    Resource(string n) : name(n) {
        cout << "Resource " << name << " 已创建" << endl;
    }
    ~Resource() {
        cout << "Resource " << name << " 已销毁" << endl;
    }
    void use() { cout << "使用 Resource " << name << endl; }
};

int main() {
    // 创建 unique_ptr
    unique_ptr<Resource> ptr1 = make_unique<Resource>("A");
    ptr1->use();

    // 不能拷贝,只能移动
    // unique_ptr<Resource> ptr2 = ptr1;  // 编译错误!
    unique_ptr<Resource> ptr2 = move(ptr1);  // 移动所有权

    if (!ptr1) {
        cout << "ptr1 已为空" << endl;
    }
    ptr2->use();

    // 离开作用域时自动释放内存
    return 0;
}

14.2 shared_ptr(共享所有权)

#include <iostream>
#include <memory>
using namespace std;

int main() {
    shared_ptr<int> sp1 = make_shared<int>(42);
    cout << "值: " << *sp1 << endl;
    cout << "引用计数: " << sp1.use_count() << endl;  // 1

    {
        shared_ptr<int> sp2 = sp1;  // 共享所有权
        cout << "引用计数: " << sp1.use_count() << endl;  // 2

        shared_ptr<int> sp3 = sp1;
        cout << "引用计数: " << sp1.use_count() << endl;  // 3
    }  // sp2 和 sp3 离开作用域

    cout << "引用计数: " << sp1.use_count() << endl;  // 1

    return 0;
}

14.3 weak_ptr(弱引用,解决循环引用)

#include <iostream>
#include <memory>
using namespace std;

class B;  // 前向声明

class A {
public:
    shared_ptr<B> bPtr;
    ~A() { cout << "A 被销毁" << endl; }
};

class B {
public:
    weak_ptr<A> aPtr;  // 使用 weak_ptr 避免循环引用
    ~B() { cout << "B 被销毁" << endl; }
};

int main() {
    {
        auto a = make_shared<A>();
        auto b = make_shared<B>();

        a->bPtr = b;
        b->aPtr = a;

        cout << "A 引用计数: " << a.use_count() << endl;
        cout << "B 引用计数: " << b.use_count() << endl;
    }  // 离开作用域,A 和 B 都能正常销毁

    cout << "作用域结束" << endl;
    return 0;
}

使用建议

  • 优先使用 unique_ptr(独占所有权,零开销)
  • 需要共享时使用 shared_ptr
  • 打破循环引用时使用 weak_ptr
  • 永远不要对同一个原始指针创建多个智能指针

15. 异常处理

异常处理机制让程序在遇到错误时能够优雅地处理,而不是直接崩溃。

15.1 基本异常处理

#include <iostream>
#include <stdexcept>
using namespace std;

double divide(double a, double b) {
    if (b == 0) {
        throw invalid_argument("除数不能为零!");
    }
    return a / b;
}

int main() {
    try {
        cout << "10 / 3 = " << divide(10, 3) << endl;
        cout << "10 / 0 = " << divide(10, 0) << endl;  // 这里会抛出异常
        cout << "这行不会执行" << endl;
    }
    catch (const invalid_argument& e) {
        cerr << "参数错误: " << e.what() << endl;
    }
    catch (const exception& e) {
        cerr << "标准异常: " << e.what() << endl;
    }
    catch (...) {
        cerr << "未知异常" << endl;
    }

    cout << "程序继续执行" << endl;
    return 0;
}

15.2 自定义异常

#include <iostream>
#include <exception>
#include <string>
using namespace std;

class InsufficientFundsException : public exception {
private:
    string message;

public:
    InsufficientFundsException(double balance, double amount)
        : message("余额不足!余额: " + to_string(balance)
                  + ", 取款: " + to_string(amount)) {}

    const char* what() const noexcept override {
        return message.c_str();
    }
};

class BankAccount {
    double balance;
public:
    BankAccount(double b) : balance(b) {}

    void withdraw(double amount) {
        if (amount > balance) {
            throw InsufficientFundsException(balance, amount);
        }
        balance -= amount;
        cout << "取款 " << amount << " 成功,余额: " << balance << endl;
    }

    double getBalance() const { return balance; }
};

int main() {
    BankAccount account(1000);

    try {
        account.withdraw(500);
        account.withdraw(300);
        account.withdraw(500);  // 余额不足
    }
    catch (const InsufficientFundsException& e) {
        cerr << "交易失败: " << e.what() << endl;
    }

    return 0;
}

15.3 异常安全与 RAII

#include <iostream>
#include <fstream>
#include <memory>
using namespace std;

// RAII:资源获取即初始化
// 资源在构造函数中获取,在析构函数中释放
class FileHandler {
private:
    ofstream file;

public:
    FileHandler(const string& filename) {
        file.open(filename);
        if (!file.is_open()) {
            throw runtime_error("无法打开文件: " + filename);
        }
        cout << "文件已打开" << endl;
    }

    ~FileHandler() {
        if (file.is_open()) {
            file.close();
            cout << "文件已关闭" << endl;
        }
    }

    void write(const string& content) {
        file << content << endl;
    }

    // 禁止拷贝
    FileHandler(const FileHandler&) = delete;
    FileHandler& operator=(const FileHandler&) = delete;
};

int main() {
    try {
        FileHandler fh("test.txt");
        fh.write("Hello, RAII!");
        fh.write("异常安全的文件操作");
    }
    catch (const exception& e) {
        cerr << "错误: " << e.what() << endl;
    }
    // 离开作用域时,即使发生异常,析构函数也会被调用,文件会被正确关闭

    return 0;
}

16. 文件操作

16.1 文本文件读写

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;

int main() {
    // ===== 写入文件 =====
    ofstream outFile("example.txt");
    if (!outFile) {
        cerr << "无法创建文件" << endl;
        return 1;
    }

    outFile << "第一行:Hello, File!" << endl;
    outFile << "第二行:C++ 文件操作" << endl;
    outFile << "第三行:这是最后一行" << endl;
    outFile.close();
    cout << "文件写入完成" << endl;

    // ===== 读取文件(逐行) =====
    ifstream inFile("example.txt");
    if (!inFile) {
        cerr << "无法打开文件" << endl;
        return 1;
    }

    string line;
    int lineNum = 1;
    cout << "\n=== 文件内容 ===" << endl;
    while (getline(inFile, line)) {
        cout << lineNum++ << ": " << line << endl;
    }
    inFile.close();

    // ===== 追加写入 =====
    ofstream appendFile("example.txt", ios::app);
    appendFile << "第四行:追加的内容" << endl;
    appendFile.close();

    return 0;
}

16.2 二进制文件读写

#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

struct Student {
    char name[50];
    int age;
    double score;
};

int main() {
    // 写入二进制文件
    Student students[] = {
        {"张三", 20, 85.5},
        {"李四", 21, 92.0},
        {"王五", 19, 78.3}
    };

    ofstream binOut("students.dat", ios::binary);
    binOut.write(reinterpret_cast<char*>(students), sizeof(students));
    binOut.close();
    cout << "二进制文件写入完成" << endl;

    // 读取二进制文件
    ifstream binIn("students.dat", ios::binary);
    Student readStudents[3];
    binIn.read(reinterpret_cast<char*>(readStudents), sizeof(readStudents));
    binIn.close();

    cout << "\n=== 读取的数据 ===" << endl;
    for (const auto& s : readStudents) {
        cout << "姓名: " << s.name
             << ", 年龄: " << s.age
             << ", 成绩: " << s.score << endl;
    }

    return 0;
}

16.3 字符串流

#include <iostream>
#include <sstream>
#include <string>
using namespace std;

int main() {
    // 字符串解析
    string data = "张三 20 85.5";
    istringstream iss(data);

    string name;
    int age;
    double score;
    iss >> name >> age >> score;

    cout << "解析结果: " << name << ", " << age << ", " << score << endl;

    // 字符串构建
    ostringstream oss;
    oss << "姓名: " << name << ", 年龄: " << age << ", 成绩: " << score;
    string result = oss.str();
    cout << "构建的字符串: " << result << endl;

    // 类型转换
    string numStr = "12345";
    int num;
    istringstream(numStr) >> num;
    cout << "转换为整数: " << num << endl;

    return 0;
}

17. 实战项目:图书管理系统

综合运用前面所学知识,实现一个功能完整的控制台图书管理系统。

17.1 项目结构

library-system/
├── main.cpp          # 主程序入口
├── Book.h            # 图书类定义
├── Library.h         # 图书馆类定义
└── utils.h           # 工具函数

17.2 完整代码

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <memory>
#include <iomanip>
#include <limits>

using namespace std;

// ==================== Book 类 ====================
class Book {
private:
    string isbn;       // ISBN 号
    string title;      // 书名
    string author;     // 作者
    string publisher;  // 出版社
    int year;          // 出版年份
    bool available;    // 是否可借

public:
    Book() : year(0), available(true) {}

    Book(string isbn, string title, string author, string publisher, int year)
        : isbn(isbn), title(title), author(author),
          publisher(publisher), year(year), available(true) {}

    // Getters
    string getISBN() const { return isbn; }
    string getTitle() const { return title; }
    string getAuthor() const { return author; }
    string getPublisher() const { return publisher; }
    int getYear() const { return year; }
    bool isAvailable() const { return available; }

    // Setters
    void setAvailable(bool status) { available = status; }

    // 显示图书信息
    void display() const {
        cout << "| " << setw(14) << left << isbn
             << "| " << setw(20) << left << title
             << "| " << setw(12) << left << author
             << "| " << setw(10) << left << year
             << "| " << setw(8) << left << (available ? "可借" : "已借出")
             << "|" << endl;
    }

    // 序列化为字符串(用于文件存储)
    string serialize() const {
        ostringstream oss;
        oss << isbn << "|" << title << "|" << author << "|"
            << publisher << "|" << year << "|" << available;
        return oss.str();
    }

    // 从字符串反序列化
    static Book deserialize(const string& data) {
        istringstream iss(data);
        string token;
        Book book;

        getline(iss, book.isbn, '|');
        getline(iss, book.title, '|');
        getline(iss, book.author, '|');
        getline(iss, book.publisher, '|');
        getline(iss, token, '|');
        book.year = stoi(token);
        getline(iss, token, '|');
        book.available = (token == "1");

        return book;
    }
};

// ==================== Library 类 ====================
class Library {
private:
    string name;
    map<string, Book> books;  // ISBN -> Book

    // 打印分隔线
    void printSeparator() const {
        cout << "+----------------+--------------------+------------+"
                "----------+--------+" << endl;
    }

    // 打印表头
    void printHeader() const {
        printSeparator();
        cout << "| " << setw(14) << left << "ISBN"
             << "| " << setw(20) << left << "书名"
             << "| " << setw(12) << left << "作者"
             << "| " << setw(10) << left << "年份"
             << "| " << setw(8) << left << "状态"
             << "|" << endl;
        printSeparator();
    }

public:
    Library(string name) : name(name) {}

    string getName() const { return name; }

    // 添加图书
    bool addBook(const Book& book) {
        if (books.count(book.getISBN()) > 0) {
            cout << "❌ 错误:ISBN " << book.getISBN() << " 已存在!" << endl;
            return false;
        }
        books[book.getISBN()] = book;
        cout << "✅ 图书《" << book.getTitle() << "》添加成功!" << endl;
        return true;
    }

    // 删除图书
    bool removeBook(const string& isbn) {
        auto it = books.find(isbn);
        if (it == books.end()) {
            cout << "❌ 未找到 ISBN 为 " << isbn << " 的图书" << endl;
            return false;
        }
        cout << "✅ 图书《" << it->second.getTitle() << "》已删除" << endl;
        books.erase(it);
        return true;
    }

    // 借书
    bool borrowBook(const string& isbn) {
        auto it = books.find(isbn);
        if (it == books.end()) {
            cout << "❌ 未找到该图书" << endl;
            return false;
        }
        if (!it->second.isAvailable()) {
            cout << "❌ 该图书已被借出" << endl;
            return false;
        }
        it->second.setAvailable(false);
        cout << "✅ 成功借出《" << it->second.getTitle() << "》" << endl;
        return true;
    }

    // 还书
    bool returnBook(const string& isbn) {
        auto it = books.find(isbn);
        if (it == books.end()) {
            cout << "❌ 未找到该图书" << endl;
            return false;
        }
        if (it->second.isAvailable()) {
            cout << "⚠️ 该图书未被借出" << endl;
            return false;
        }
        it->second.setAvailable(true);
        cout << "✅ 成功归还《" << it->second.getTitle() << "》" << endl;
        return true;
    }

    // 按书名搜索
    void searchByTitle(const string& keyword) const {
        printHeader();
        int count = 0;
        for (const auto& pair : books) {
            if (pair.second.getTitle().find(keyword) != string::npos) {
                pair.second.display();
                count++;
            }
        }
        printSeparator();
        cout << "找到 " << count << " 本图书" << endl;
    }

    // 按作者搜索
    void searchByAuthor(const string& keyword) const {
        printHeader();
        int count = 0;
        for (const auto& pair : books) {
            if (pair.second.getAuthor().find(keyword) != string::npos) {
                pair.second.display();
                count++;
            }
        }
        printSeparator();
        cout << "找到 " << count << " 本图书" << endl;
    }

    // 显示所有图书
    void displayAll() const {
        if (books.empty()) {
            cout << "📚 图书馆暂无藏书" << endl;
            return;
        }
        cout << "\n📚 " << name << " - 馆藏图书列表" << endl;
        printHeader();
        for (const auto& pair : books) {
            pair.second.display();
        }
        printSeparator();
        cout << "共 " << books.size() << " 本图书" << endl;
    }

    // 显示统计信息
    void displayStats() const {
        int total = books.size();
        int available = 0;
        for (const auto& pair : books) {
            if (pair.second.isAvailable()) available++;
        }
        cout << "\n📊 统计信息:" << endl;
        cout << "   总藏书量: " << total << endl;
        cout << "   可借数量: " << available << endl;
        cout << "   已借出:   " << (total - available) << endl;
    }

    // 保存到文件
    bool saveToFile(const string& filename) const {
        ofstream file(filename);
        if (!file) {
            cerr << "❌ 无法打开文件进行写入" << endl;
            return false;
        }
        for (const auto& pair : books) {
            file << pair.second.serialize() << endl;
        }
        file.close();
        cout << "✅ 数据已保存到 " << filename << endl;
        return true;
    }

    // 从文件加载
    bool loadFromFile(const string& filename) {
        ifstream file(filename);
        if (!file) {
            cout << "⚠️ 数据文件不存在,将创建新图书馆" << endl;
            return false;
        }

        string line;
        int count = 0;
        while (getline(file, line)) {
            if (!line.empty()) {
                Book book = Book::deserialize(line);
                books[book.getISBN()] = book;
                count++;
            }
        }
        file.close();
        cout << "✅ 从文件加载了 " << count << " 本图书" << endl;
        return true;
    }
};

// ==================== 工具函数 ====================
void clearInput() {
    cin.clear();
    cin.ignore(numeric_limits<streamsize>::max(), '\n');
}

void pauseScreen() {
    cout << "\n按 Enter 键继续...";
    clearInput();
    cin.get();
}

// ==================== 主菜单 ====================
void showMenu() {
    cout << "\n╔══════════════════════════════════════╗" << endl;
    cout << "║       📚 图书管理系统 v1.0          ║" << endl;
    cout << "╠══════════════════════════════════════╣" << endl;
    cout << "║  1. 添加图书                         ║" << endl;
    cout << "║  2. 删除图书                         ║" << endl;
    cout << "║  3. 借阅图书                         ║" << endl;
    cout << "║  4. 归还图书                         ║" << endl;
    cout << "║  5. 按书名搜索                       ║" << endl;
    cout << "║  6. 按作者搜索                       ║" << endl;
    cout << "║  7. 显示所有图书                     ║" << endl;
    cout << "║  8. 统计信息                         ║" << endl;
    cout << "║  9. 保存数据                         ║" << endl;
    cout << "║  0. 退出系统                         ║" << endl;
    cout << "╚══════════════════════════════════════╝" << endl;
    cout << "请选择操作 (0-9): ";
}

int main() {
    Library library("我的图书馆");
    const string dataFile = "library_data.txt";

    // 启动时自动加载数据
    library.loadFromFile(dataFile);

    int choice;
    bool running = true;

    while (running) {
        showMenu();
        cin >> choice;

        if (cin.fail()) {
            clearInput();
            cout << "❌ 请输入有效的数字!" << endl;
            continue;
        }
        clearInput();

        switch (choice) {
            case 1: {
                // 添加图书
                string isbn, title, author, publisher;
                int year;

                cout << "\n--- 添加图书 ---" << endl;
                cout << "ISBN: "; getline(cin, isbn);
                cout << "书名: "; getline(cin, title);
                cout << "作者: "; getline(cin, author);
                cout << "出版社: "; getline(cin, publisher);
                cout << "出版年份: "; cin >> year;
                clearInput();

                Book book(isbn, title, author, publisher, year);
                library.addBook(book);
                pauseScreen();
                break;
            }
            case 2: {
                // 删除图书
                string isbn;
                cout << "\n--- 删除图书 ---" << endl;
                cout << "请输入 ISBN: "; getline(cin, isbn);
                library.removeBook(isbn);
                pauseScreen();
                break;
            }
            case 3: {
                // 借阅
                string isbn;
                cout << "\n--- 借阅图书 ---" << endl;
                cout << "请输入 ISBN: "; getline(cin, isbn);
                library.borrowBook(isbn);
                pauseScreen();
                break;
            }
            case 4: {
                // 归还
                string isbn;
                cout << "\n--- 归还图书 ---" << endl;
                cout << "请输入 ISBN: "; getline(cin, isbn);
                library.returnBook(isbn);
                pauseScreen();
                break;
            }
            case 5: {
                // 按书名搜索
                string keyword;
                cout << "\n--- 书名搜索 ---" << endl;
                cout << "请输入关键词: "; getline(cin, keyword);
                library.searchByTitle(keyword);
                pauseScreen();
                break;
            }
            case 6: {
                // 按作者搜索
                string keyword;
                cout << "\n--- 作者搜索 ---" << endl;
                cout << "请输入关键词: "; getline(cin, keyword);
                library.searchByAuthor(keyword);
                pauseScreen();
                break;
            }
            case 7: {
                library.displayAll();
                pauseScreen();
                break;
            }
            case 8: {
                library.displayStats();
                pauseScreen();
                break;
            }
            case 9: {
                library.saveToFile(dataFile);
                pauseScreen();
                break;
            }
            case 0: {
                // 退出前自动保存
                library.saveToFile(dataFile);
                cout << "\n👋 感谢使用图书管理系统,再见!" << endl;
                running = false;
                break;
            }
            default:
                cout << "❌ 无效选择,请重新输入" << endl;
                pauseScreen();
        }
    }

    return 0;
}

17.3 项目知识点总结

这个实战项目综合运用了以下 C++ 知识点:

知识点 在项目中的应用
类与对象 Book 类和 Library 类的设计
封装 private 成员 + public 接口
STL 容器 map 存储图书,vector 管理数据
字符串处理 stringstringstream 的使用
文件操作 图书数据的保存与加载
异常处理 文件操作的错误处理
智能指针 可扩展为 shared_ptr 管理图书对象
算法 findcount_if 等搜索统计
格式化输出 setwleft 对齐输出

17.4 编译与运行

g++ -std=c++17 -o library main.cpp
./library

18. 学习建议与进阶路线

学习建议

  1. 多写代码:光看不练是学不会编程的,每个知识点都要亲手敲代码
  2. 理解原理:不要只记语法,要理解背后的原理(如内存模型、编译过程)
  3. 善用调试:学会使用 gdb 或 IDE 的调试器,观察程序运行过程
  4. 阅读优秀代码:GitHub 上有很多高质量的 C++ 开源项目
  5. 循序渐进:不要跳过基础直接学高级特性

进阶路线

基础语法 → 面向对象 → STL → 模板 → 现代C++(C++11/14/17/20)
                                          ↓
                              并发编程 ← 内存模型 ← 智能指针
                                          ↓
                              设计模式 ← 性能优化 ← 系统编程

推荐学习资源

书籍:

  • 《C++ Primer》(第5版)—— 最经典的 C++ 入门书
  • 《Effective C++》—— 55 条改善程序设计的建议
  • 《C++ 标准库》—— 全面的 STL 参考
  • 《STL 源码剖析》—— 深入理解 STL 实现

在线资源:

练习项目建议

难度 项目 涉及知识点
学生成绩管理系统 类、文件IO、基本算法
⭐⭐ 简易计算器 运算符重载、异常处理
⭐⭐ 贪吃蛇游戏 二维数组、循环、条件判断
⭐⭐⭐ 通讯录管理系统 STL容器、文件操作、搜索
⭐⭐⭐ 简易 HTTP 服务器 网络编程、多线程
⭐⭐⭐⭐ 数据库引擎 文件系统、B树、序列化
⭐⭐⭐⭐⭐ 游戏引擎 设计模式、内存管理、性能优化

写在最后:C++ 是一门博大精深的语言,即使是经验丰富的开发者也在不断学习。保持好奇心,持续实践,你一定能掌握它。祝你学习愉快!🚀

内容声明

本文内容为AI技术学习教程,仅供学习参考。如涉及技术问题,欢迎通过 xurj005@163.com 与我们交流。

目录