C++ 1.面向对象编程(OOP)框架
目录
面向对象编程(OOP)框架
问题背景
OOP框架开发的关键问题解析
步骤1:抽象设计阶段
步骤2:继承层次设计
步骤3:多态机制应用
步骤4:对象关系管理
这个案例展现的核心OOP价值
封装的价值
继承的价值
多态的价值
实际开发中的应用场景
GUI框架开发
游戏开发
文件系统
扩展:现在C++ VS C++98:
内存管理差异
循环语法差异
函数对象vs Lambda
扩展:多态语法详细解析
核心多态语法要素
智能指针容器
Lambda表达式
多态调用机制
多态的工作原理
dynamic_cast安全转换
面向对象编程(OOP)框架
封装: 将数据(属性)和操作这些数据的函数(方法)捆绑在一个单元(类)内部。同时,对内部实现的细节进行隐藏,只暴露有限的、必要的接口(方法)与外部进行交互。
继承:允许基于一个现有的类(父类/超类/基类)定义一个新的类(子类/派生类)。子类自动获得父类的属性和方法(除了private
的,通常需要通过protected
或公共方法访问),并可以添加自己特有的属性和方法,或者重写父类已有的方法以满足特定需求。
多态:字面意思是“多种形态”。在 OOP 中,指同一个接口(或父类型的引用) 可以使用不同的底层实现方式(子类的具体方法)。更具体地说,它允许你将子类对象视为父类对象(向上转型),当通过父类的引用调用一个被重写的方法时,实际执行的是子类版本的那个方法。
我将通过一个完整的案例来演示面向对象编程框架的逐步开发过程。让我们以图书管理系统为例,这个案例能很好地展现OOP的核心概念和问题解决思路。
问题背景
我们需要开发一个图书管理系统,管理不同类型的图书(普通书籍、期刊、电子书),处理借阅、归还、查询等操作。这个案例展现了以下OOP核心问题:
-
如何抽象现实世界的概念
-
如何处理相似但不同的对象类型
-
如何实现代码复用和扩展性
-
如何管理对象之间的关系
案例:
// library_project.cpp :
// 时间:2025.7.28#include <iostream>
#include <iomanip>
#include <string.h>
#include <vector>
// ==============================================
// 步骤1: 数据抽象 - 解决"什么是对象"的问题
// ==============================================// 问题:现实中的图书有哪些共同属性和行为?
// 解决:创建抽象基类定义通用接口class Book {
protected:// 封装:将数据成员设为protected,允许子类访问但外部不能直接访问std::string isbn; // 书籍编号std::string title; // 书名std::string author; // 作者double price; // 价格bool isAvailable; // 是否可以借用public:// 构造函数:对象创建时的初始化,使用初始化列表提高效率,避免先构造再赋值// 1.有参构造,没有设计无参数构造 确保构造函数接受4个参数:isbn, title, author, priceBook(const std::string& isbn, const std::string& title,const std::string& author, double& price):isbn(isbn), title(title), author(author), price(price), isAvailable(true){std::cout << "创建图书:" << std::endl;}// 虚析构函数:确保派生类对象能正确析构// 问题:如果基类析构函数不是虚函数,通过基类指针删除派生类对象会有问题virtual ~Book() {std::cout << "销毁图书: " << title << std::endl;}// 纯虚函数:定义接口,子类必须实现// 问题:不同类型的书籍展示信息的方式可能不同virtual void displayInfo() const = 0;// 虚函数:提供默认实现,子类可以重写// 问题:不同类型书籍的借阅规则可能不同(可借不可借阅)virtual bool canBorrow() const {return isAvailable;}// 虚函数:计算借阅费用,不同类型可能有不同计费方式virtual double calculateFee(int days) const{return 0.0; // 默认免费}// 重要:添加虚函数borrow(),这样派生类才能重写它virtual void borrow(){if (canBorrow()){isAvailable = false;std::cout << "<<"<<title<<">>" << "已经被借阅出去" << std::endl;}else{std::cout << "<<" << title << ">>" << "不可借阅" << std::endl;}}void returnBook(){isAvailable = true;std::cout << "<<" << title << ">>" << "已归还" << std::endl;}std::string getIsbn(){return isbn;}std::string getTitle(){return title;}std::string getAuthor(){return author;}double getPrice(){return price;}bool getAvailability(){return isAvailable;}
};// ==============================================
// 步骤2: 继承 - 解决"代码复用"的问题
// ==============================================// 问题:普通图书、期刊、电子书都是书,但有各自特点
// 解决:通过继承复用基类代码,通过重写体现差异// 普通图书类
class PhysicalBook :public Book {
private:std::string location; // 存放位置int pages; // 页数public:// 构造函数:调用基类构造函数初始化公共属性// 正确调用基类构造函数(4个参数)PhysicalBook(const std::string& isbn, const std::string& title,const std::string& author, double price,const std::string& location, int pages): Book(isbn, title, author, price), // 传递4个参数给基类location(location), pages(pages) {std::cout << "创建实体书,位置: " << location << std::endl;}// 重写纯虚函数:实现具体的显示逻辑void displayInfo() const override{std::cout << "=== 实体图书信息 ===" << std::endl;std::cout << "ISBN: " << isbn << std::endl;std::cout << "书名: " << title << std::endl;std::cout << "作者: " << author << std::endl;std::cout << "价格: " << std::fixed << std::setprecision(2) << price << std::endl;std::cout << "存放位置: " << location << std::endl;std::cout << "页数: " << pages << std::endl;std::cout << "状态: " << (isAvailable ? "可借" : "已借出") << std::endl;}// 重写虚函数:实体书可能有借阅费double calculateFee(int days) const override{if (days > 30){return (days - 30) * 0.5; // 超过30天后的每天按照0.5元计算}return 0.0;}// 子类特有方法std::string getLocation() const{return location;}int getPages() const{return pages;}
};// 期刊类
class Magazine : public Book {
private:int issueNumber; // 期号std::string publishDate; // 出版日期public:Magazine(const std::string& isbn, const std::string& title,const std::string& author, double price,int issueNumber, const std::string& publishDate): Book(isbn, title, author, price), issueNumber(issueNumber), publishDate(publishDate) {}void displayInfo() const override {std::cout << "=== 期刊信息 ===" << std::endl;std::cout << "ISBN: " << isbn << std::endl;std::cout << "期刊名: " << title << std::endl;std::cout << "主编: " << author << std::endl;std::cout << "价格: " << std::fixed << std::setprecision(2) << price << std::endl;std::cout << "期号: " << issueNumber << std::endl;std::cout << "出版日期: " << publishDate << std::endl;std::cout << "状态: " << (isAvailable ? "可借" : "已借出") << std::endl;}// 期刊通常不能长期借阅bool canBorrow() const override {return isAvailable; // 可以添加额外的期刊借阅规则}double calculateFee(int days) const override {if (days > 7) {return (days - 7) * 1.0; // 期刊超过7天每天1元}return 0.0;}
};// 电子书类
class EBook : public Book {
private:std::string format; // 文件格式double fileSize; // 文件大小(MB)std::string downloadUrl; // 下载链接public:EBook(const std::string& isbn, const std::string& title,const std::string& author, double price,const std::string& format, double fileSize, const std::string& downloadUrl): Book(isbn, title, author, price), format(format), fileSize(fileSize), downloadUrl(downloadUrl) {}void displayInfo() const override {std::cout << "=== 电子书信息 ===" << std::endl;std::cout << "ISBN: " << isbn << std::endl;std::cout << "书名: " << title << std::endl;std::cout << "作者: " << author << std::endl;std::cout << "价格: " << std::fixed << std::setprecision(2) << price << std::endl;std::cout << "格式: " << format << std::endl;std::cout << "文件大小: " << fileSize << "MB" << std::endl;std::cout << "下载地址: " << downloadUrl << std::endl;std::cout << "状态: " << (isAvailable ? "可下载" : "暂不可用") << std::endl;}// 电子书借阅逻辑不同:可以同时被多人"借阅"bool canBorrow() const override {return true; // 电子书总是可以借阅}void borrow() override {std::cout << "正在下载电子书: " << title << std::endl;std::cout << "下载链接: " << downloadUrl << std::endl;// 注意:电子书不改变isAvailable状态}// 电子书特有方法void download() const {std::cout << "开始下载 " << "<<" << title << ">>" << " (" << format << ", " << fileSize << "MB)" << std::endl;}
};// ==============================================
// 步骤3: 多态 - 解决"统一接口处理不同对象"的问题
// ==============================================// 问题:如何用统一的方式处理不同类型的书籍?
// 解决:通过多态,使用基类指针或引用调用派生类的重写方法class LibraryManager
{
private:// 使用智能指针管理书籍对象,自动内存管理std::vector<std::unique_ptr<Book>> books;
public:void addBook(std::unique_ptr<Book> book){books.push_back(std::move(book));std::cout << "书籍已添加到图书馆" << std::endl;}// 显示所有书籍:多态调用每个对象的displayInfo方法void displayAllBooks() const{std::cout << "\n========== 图书馆藏书 ==========" << std::endl;for (const auto& book : books) {book->displayInfo(); // 多态调用:运行时决定调用哪个版本std::cout << std::endl;}}// 按ISBN查找书籍Book* findBookByIsbn(const std::string & isbn) {// 使用STL算法和lambda表达式auto it = std::find_if(books.begin(), books.end(),[&isbn](const std::unique_ptr<Book>& book) {return book->getIsbn() == isbn;});return (it != books.end()) ? it->get() : nullptr;}// 借阅书籍:统一接口处理不同类型bool borrowBook(const std::string& isbn) {Book* book = findBookByIsbn(isbn);if (book && book->canBorrow()) {book->borrow(); // 多态调用return true;}std::cout << "无法借阅书籍 ISBN: " << isbn << std::endl;return false;}// 归还书籍bool returnBook(const std::string& isbn) {Book* book = findBookByIsbn(isbn);if (book) {book->returnBook();return true;}std::cout << "未找到书籍 ISBN: " << isbn << std::endl;return false;}// 计算所有已借书籍的费用void calculateAllFees(int days) const {std::cout << "\n========== 借阅费用计算 ==========" << std::endl;double totalFee = 0.0;for (const auto& book : books) {if (!book->getAvailability()) { // 已借出的书double fee = book->calculateFee(days); // 多态调用std::cout << book->getTitle() << ": ¥"<< std::fixed << std::setprecision(2) << fee << std::endl;totalFee += fee;}}std::cout << "总费用: " << std::fixed << std::setprecision(2) << totalFee << std::endl;}// 统计不同类型书籍数量void showStatistics() const {int physicalCount = 0, magazineCount = 0, ebookCount = 0;for (const auto& book : books) {// 使用dynamic_cast进行安全的向下转型if (dynamic_cast<PhysicalBook*>(book.get())) {physicalCount++;}else if (dynamic_cast<Magazine*>(book.get())) {magazineCount++;}else if (dynamic_cast<EBook*>(book.get())) {ebookCount++;}}std::cout << "\n========== 图书馆统计 ==========" << std::endl;std::cout << "实体图书: " << physicalCount << " 本" << std::endl;std::cout << "期刊: " << magazineCount << " 本" << std::endl;std::cout << "电子书: " << ebookCount << " 本" << std::endl;std::cout << "总计: " << books.size() << " 本" << std::endl;}
};// ==============================================
// 步骤4: 组合和关联 - 解决"对象间关系"的问题
// ==============================================// 问题:用户和图书之间的借阅关系如何建模?
// 解决:通过组合关系管理用户的借阅记录class User
{
private:std::string userId;std::string name;std::vector<std::string> borrowedBooks; // 存储借阅的书籍的ISBN
public:User(const std::string& userId,const std::string &name):userId(userId),name(name){}void borrowBook(const std::string& isbn) {borrowedBooks.push_back(isbn);}void returnBook(const std::string& isbn) {auto it = std::find(borrowedBooks.begin(), borrowedBooks.end(), isbn);if (it != borrowedBooks.end()) {borrowedBooks.erase(it);}}void showBorrowedBooks() const {std::cout << "\n用户 " << name << " 的借阅记录:" << std::endl;if (borrowedBooks.empty()) {std::cout << "暂无借阅记录" << std::endl;}else {for (const auto& isbn : borrowedBooks) {std::cout << "- ISBN: " << isbn << std::endl;}}}std::string getUserId() const { return userId; }std::string getName() const { return name; }int getBorrowedCount() const { return borrowedBooks.size(); }};// ==============================================
// 主函数:演示OOP框架的完整使用
// ==============================================int main()
{std::cout << "========== OOP图书管理系统演示 ==========" << std::endl;// 创建图书馆管理器LibraryManager library;std::cout << "\n--- 步骤1: 创建不同类型的书籍 ---" << std::endl;// 使用make_unique创建智能指针,自动内存管理library.addBook(std::make_unique<PhysicalBook>("978-0201633610", "设计模式", "Gang of Four", 89.90, "A区-3层", 395));library.addBook(std::make_unique<Magazine>("ISSN-1234-5678", "程序员杂志", "编辑部", 25.00, 202401, "2024-01-15"));library.addBook(std::make_unique<EBook>("978-1449355739", "JavaScript权威指南", "David Flanagan", 119.00,"PDF", 15.2, "https://example.com/download/js-guide"));std::cout << "\n--- 步骤2: 多态展示所有书籍信息 ---" << std::endl;library.displayAllBooks();std::cout << "\n--- 步骤3: 统计信息展示 ---" << std::endl;library.showStatistics();std::cout << "\n--- 步骤4: 借阅操作演示 ---" << std::endl;library.borrowBook("978-0201633610"); // 借实体书library.borrowBook("ISSN-1234-5678"); // 借期刊library.borrowBook("978-1449355739"); // 借电子书std::cout << "\n--- 步骤5: 费用计算演示 ---" << std::endl;library.calculateAllFees(45); // 计算借阅45天的费用std::cout << "\n--- 步骤5: 费用计算演示 ---" << std::endl;library.calculateAllFees(45); // 计算借阅45天的费用std::cout << "\n--- 步骤6: 归还操作演示 ---" << std::endl;library.returnBook("978-0201633610");std::cout << "\n--- 步骤7: 用户管理演示 ---" << std::endl;User user1("U001", "张三");user1.borrowBook("978-0201633610");user1.borrowBook("ISSN-1234-5678");user1.showBorrowedBooks();std::cout << "\n========== 演示结束 ==========" << std::endl;return 0;
}/*
OOP解决的核心问题总结:1. 抽象问题:如何将现实世界的概念转化为程序对象- 通过类定义对象的属性和行为- 通过抽象基类定义通用接口2. 复用问题:如何避免重复代码- 通过继承复用基类的代码- 通过虚函数实现接口的不同实现3. 扩展问题:如何在不修改现有代码的情况下添加新功能- 通过多态支持新的派生类- 通过虚函数机制支持功能扩展4. 维护问题:如何让代码更容易理解和修改- 通过封装隐藏实现细节- 通过清晰的继承层次组织代码结构5. 复杂性问题:如何管理大型程序的复杂性- 通过对象间的组合和关联建立关系- 通过统一的接口降低模块间的耦合
*/
运行截图(部分):
OOP框架开发的关键问题解析
步骤1:抽象设计阶段
问题:如何识别和抽象现实世界的概念?
-
解决思路:找出对象的共同属性和行为,定义抽象基类
-
语法要点:
-
virtual
函数:支持多态调用 -
= 0
纯虚函数:强制子类实现 -
protected
访问控制:允许子类访问但外部不能
-
步骤2:继承层次设计
问题:如何平衡代码复用和灵活性?
-
解决思路:通过继承复用公共代码,通过重写体现差异
-
语法要点:
-
override
关键字:确保正确重写 -
构造函数初始化列表:高效初始化基类
-
虚析构函数:确保正确的多态析构
-
步骤3:多态机制应用
问题:如何用统一的接口处理不同类型的对象?
-
解决思路:使用基类指针/引用,运行时绑定具体实现
-
核心优势:
-
开闭原则:对扩展开放,对修改封闭
-
统一处理:一套代码处理多种类型
-
动态绑定:运行时决定调用哪个版本
-
步骤4:对象关系管理
问题:如何建模复杂的对象间关系?
-
组合关系:整体包含部分(图书馆包含书籍)
-
关联关系:对象间的使用关系(用户借阅书籍)
-
聚合关系:松散的包含关系
这个案例展现的核心OOP价值
封装的价值
// 数据保护:外部不能直接修改内部状态
private: bool isAvailable; // 只能通过方法修改// 接口稳定:实现可以改变,接口保持不变
public:void borrow(); // 外部只需知道如何调用
继承的价值
// 代码复用:避免重复实现相同功能
class PhysicalBook : public Book { // 继承所有基本功能// 只需实现特有的部分void displayInfo() const override;
};
多态的价值
// 统一处理:一个接口处理多种类型
void LibraryManager::displayAllBooks() {for (const auto& book : books) {book->displayInfo(); // 自动调用正确的版本}
}
实际开发中的应用场景
GUI框架开发
-
基类:
Widget
-
派生类:
Button
,TextBox
,Menu
-
多态:统一的事件处理机制
游戏开发
-
基类:
GameObject
-
派生类:
Player
,Enemy
,Item
-
多态:统一的更新和渲染循环
文件系统
-
基类:
FileSystemNode
-
派生类:
File
,Directory
-
多态:统一的操作接口
这个案例完整展现了OOP如何解决软件开发中的核心问题:抽象建模、代码复用、系统扩展、复杂性管理。通过逐步构建,我们看到了OOP三大特性如何协同工作,创建出既灵活又可维护的软件架构。
扩展:现在C++ VS C++98:
// ========================================
// 现代C++版本 - 详细语法解释
// ========================================class ModernLibraryManager {
private:// 语法解释1: std::vector<std::unique_ptr<Book>>// - std::vector: 动态数组容器// - std::unique_ptr<Book>: 智能指针,自动管理内存// - Book: 基类类型,实现多态存储std::vector<std::unique_ptr<Book>> books;public:// 语法解释2: std::unique_ptr<Book> book 参数// - unique_ptr表示独占所有权// - 传入时转移所有权给容器void addBook(std::unique_ptr<Book> book) {// 语法解释3: std::move(book)// - 移动语义,转移所有权而不是拷贝// - 避免不必要的拷贝操作,提高效率books.push_back(std::move(book));std::cout << "书籍已添加到图书馆" << std::endl;}// 语法解释4: 基于范围的for循环 (C++11)void displayAllBooks() const {std::cout << "\n========== 图书馆藏书 ==========" << std::endl;// for (const auto& book : books) 语法解释:// - const: 不修改元素// - auto: 自动类型推导,这里是 std::unique_ptr<Book>&// - &: 引用,避免拷贝// - book: 循环变量名// - books: 要遍历的容器for (const auto& book : books) {// 语法解释5: book->displayInfo() 多态调用// - book是基类指针// - 运行时根据实际对象类型调用对应的方法// - 这就是多态的核心机制book->displayInfo();std::cout << std::endl;}}// 语法解释6: STL算法 + Lambda表达式Book* findBookByIsbn(const std::string& isbn) {// std::find_if语法解释:// - books.begin(), books.end(): 迭代器范围// - lambda表达式: [&isbn](const std::unique_ptr<Book>& book)// Lambda表达式详解:// [&isbn] - 捕获列表,按引用捕获isbn变量// (const std::unique_ptr<Book>& book) - 参数列表// { return book->getIsbn() == isbn; } - 函数体auto it = std::find_if(books.begin(), books.end(),[&isbn](const std::unique_ptr<Book>& book) {return book->getIsbn() == isbn;});// 语法解释7: 三元操作符 + 智能指针// (it != books.end()) ? it->get() : nullptr// - it->get(): 从unique_ptr获取原始指针// - 条件判断:找到返回指针,否则返回nullptrreturn (it != books.end()) ? it->get() : nullptr;}// 语法解释8: 多态方法调用bool borrowBook(const std::string& isbn) {Book* book = findBookByIsbn(isbn);if (book && book->canBorrow()) {// 多态调用解释:// - book是Book*类型的指针// - 实际指向的可能是PhysicalBook或EBook对象// - 运行时根据实际对象类型调用对应的borrow()方法book->borrow(); // 多态调用!return true;}std::cout << "无法借阅书籍 ISBN: " << isbn << std::endl;return false;}// 语法解释9: dynamic_cast 安全类型转换void showStatistics() const {int physicalCount = 0, ebookCount = 0;for (const auto& book : books) {// dynamic_cast语法解释:// - dynamic_cast<PhysicalBook*>(book.get())// - 尝试将基类指针转换为派生类指针// - 成功返回有效指针,失败返回nullptr// - 只能用于有虚函数的类(多态类)if (dynamic_cast<PhysicalBook*>(book.get())) {physicalCount++;} else if (dynamic_cast<EBook*>(book.get())) {ebookCount++;}}std::cout << "\n========== 图书馆统计 ==========" << std::endl;std::cout << "实体图书: " << physicalCount << " 本" << std::endl;std::cout << "电子书: " << ebookCount << " 本" << std::endl;std::cout << "总计: " << books.size() << " 本" << std::endl;}
};
C++ 98 :
// ========================================
// C++98版本实现 - 相同功能,不同语法
// ========================================// C++98版本的函数对象(替代lambda)
class IsbnMatcher {
private:const std::string& target_isbn;
public:// 构造函数接受要查找的ISBNIsbnMatcher(const std::string& isbn) : target_isbn(isbn) {}// 重载operator()使其可以像函数一样被调用bool operator()(const Book* book) const {return book->getIsbn() == target_isbn;}
};class Cpp98LibraryManager {
private:// C++98语法1: 使用原始指针代替智能指针// - 需要手动管理内存// - 需要在析构函数中删除所有指针std::vector<Book*> books;public:// C++98构造函数Cpp98LibraryManager() {std::cout << "C++98图书馆管理系统初始化" << std::endl;}// C++98析构函数 - 手动清理内存~Cpp98LibraryManager() {std::cout << "清理图书馆资源..." << std::endl;// 手动删除所有书籍对象for (std::vector<Book*>::iterator it = books.begin(); it != books.end(); ++it) {delete *it; // 释放内存}books.clear();}// C++98语法2: 接受原始指针,手动管理所有权void addBook(Book* book) {if (book) {books.push_back(book);std::cout << "书籍已添加到图书馆" << std::endl;}}// C++98语法3: 传统for循环代替基于范围的for循环void displayAllBooks() const {std::cout << "\n========== 图书馆藏书 ==========" << std::endl;// 传统for循环语法:// - size_t i = 0: 初始化// - i < books.size(): 循环条件// - ++i: 每次迭代后执行for (size_t i = 0; i < books.size(); ++i) {// C++98访问元素:books[i] 或 books.at(i)books[i]->displayInfo(); // 多态调用std::cout << std::endl;}// 或者使用迭代器(C++98推荐方式):std::cout << "--- 使用迭代器遍历 ---" << std::endl;for (std::vector<Book*>::const_iterator it = books.begin();it != books.end(); ++it) {(*it)->displayInfo(); // 解引用迭代器得到指针,再调用方法std::cout << std::endl;}}// C++98语法4: 使用函数对象代替lambda表达式Book* findBookByIsbn(const std::string& isbn) {// 创建函数对象IsbnMatcher matcher(isbn);// 使用std::find_if和函数对象std::vector<Book*>::iterator it = std::find_if(books.begin(), books.end(), matcher // 函数对象代替lambda);// C++98条件判断if (it != books.end()) {return *it; // 解引用迭代器得到指针} else {return NULL; // C++98使用NULL而不是nullptr}}// C++98语法5: 多态调用(与现代C++相同)bool borrowBook(const std::string& isbn) {Book* book = findBookByIsbn(isbn);if (book && book->canBorrow()) {book->borrow(); // 多态调用!语法相同return true;}std::cout << "无法借阅书籍 ISBN: " << isbn << std::endl;return false;}bool returnBook(const std::string& isbn) {Book* book = findBookByIsbn(isbn);if (book) {book->returnBook(); // 多态调用return true;}std::cout << "未找到书籍 ISBN: " << isbn << std::endl;return false;}// C++98语法6: 计算费用(展示多态的威力)void calculateAllFees(int days) const {std::cout << "\n========== 借阅费用计算 ==========" << std::endl;double totalFee = 0.0;// 传统for循环for (size_t i = 0; i < books.size(); ++i) {Book* book = books[i];if (!book->getAvailability()) { // 已借出的书// 多态调用:不同类型的书有不同的计费方式double fee = book->calculateFee(days);std::cout << book->getTitle() << ": " << std::fixed << std::setprecision(2) << fee << "元" << std::endl;totalFee += fee;}}std::cout << "总费用: " << std::fixed << std::setprecision(2) << totalFee << "元" << std::endl;}// C++98语法7: dynamic_cast(C++98支持,但使用方式相同)void showStatistics() const {int physicalCount = 0, ebookCount = 0;// 使用传统for循环for (size_t i = 0; i < books.size(); ++i) {Book* book = books[i];// dynamic_cast在C++98中语法相同// 但需要确保编译器支持RTTI(运行时类型信息)if (dynamic_cast<PhysicalBook*>(book)) {physicalCount++;} else if (dynamic_cast<EBook*>(book)) {ebookCount++;}}std::cout << "\n========== 图书馆统计 ==========" << std::endl;std::cout << "实体图书: " << physicalCount << " 本" << std::endl;std::cout << "电子书: " << ebookCount << " 本" << std::endl;std::cout << "总计: " << books.size() << " 本" << std::endl;}// C++98辅助方法:获取书籍数量size_t getBookCount() const {return books.size();}
};
内存管理差异
// C++98 - 手动内存管理
class Cpp98LibraryManager {
private:std::vector<Book*> books; // 原始指针public:~Cpp98LibraryManager() {// 手动删除所有对象for (std::vector<Book*>::iterator it = books.begin(); it != books.end(); ++it) {delete *it;}}
};// 现代C++ - 自动内存管理
class ModernLibraryManager {
private:std::vector<std::unique_ptr<Book>> books; // 智能指针// 析构函数自动调用,无需手动delete
};
循环语法差异
// C++98 - 传统for循环
for (size_t i = 0; i < books.size(); ++i) {books[i]->displayInfo(); // 多态调用
}// 或使用迭代器
for (std::vector<Book*>::iterator it = books.begin();it != books.end(); ++it) {(*it)->displayInfo();
}// 现代C++ - 基于范围的for循环
for (const auto& book : books) {book->displayInfo(); // 语法更简洁
}
函数对象vs Lambda
// C++98 - 函数对象
class IsbnMatcher {const std::string& target_isbn;
public:IsbnMatcher(const std::string& isbn) : target_isbn(isbn) {}bool operator()(const Book* book) const {return book->getIsbn() == target_isbn;}
};// 使用方式
IsbnMatcher matcher(isbn);
std::find_if(books.begin(), books.end(), matcher);// 现代C++ - Lambda表达式
std::find_if(books.begin(), books.end(),[&isbn](const std::unique_ptr<Book>& book) {return book->getIsbn() == isbn;});
扩展:多态语法详细解析
核心多态语法要素
智能指针容器
// 现代C++ (C++11+)
std::vector<std::unique_ptr<Book>> books;
// ↑ ↑
// 智能指针类型 基类类型// 语法解析:
// - std::unique_ptr<Book>: 独占所有权的智能指针
// - 自动管理内存,析构时自动删除对象
// - 基类指针实现多态存储
移动语义
// std::move(book) 语法解析:
void addBook(std::unique_ptr<Book> book) {books.push_back(std::move(book));// ↑// 移动语义:转移所有权而不是拷贝// 避免不必要的拷贝操作,提高效率
}
Lambda表达式
// Lambda语法解析:
[&isbn](const std::unique_ptr<Book>& book) { return book->getIsbn() == isbn; }
// ↑ ↑ ↑
//捕获列表 参数列表 函数体// [&isbn] - 按引用捕获外部变量isbn
// (const std::unique_ptr<Book>& book) - 参数类型
// { return ... } - 返回bool的函数体
多态调用机制
book->displayInfo(); // 多态调用// 运行时过程:
// 1. book是基类指针,指向派生类对象
// 2. 通过虚函数表(vtable)查找实际函数地址
// 3. 调用派生类的重写版本
多态的工作原理
虚函数表机制
// 编译器为每个类生成虚函数表
class Book {virtual void displayInfo() = 0; // 虚函数// 编译器添加:void* vptr; // 虚函数表指针
};// 运行时调用过程:
Book* book = new PhysicalBook(...);
book->displayInfo();// 实际执行:
// 1. 通过book->vptr找到虚函数表
// 2. 在表中查找displayInfo的地址
// 3. 调用PhysicalBook::displayInfo()
dynamic_cast安全转换
// 语法:dynamic_cast<目标类型*>(源指针)
PhysicalBook* physical = dynamic_cast<PhysicalBook*>(book);if (physical) {// 转换成功,可以安全使用std::cout << physical->getLocation() << std::endl;
} else {// 转换失败,book不是PhysicalBook类型std::cout << "不是实体书" << std::endl;
}
关键语法对比表
功能 | C++98 | 现代C++ |
指针管理 | Book* | std::unique_ptr<Book> |
内存释放 | delete ptr | 自动释放 |
空指针 | NULL | nullptr |
类型推导 | 显式声明 | auto |
循环遍历 | 传统for | 基于范围for |
函数对象 | 类+operator() | Lambda表达式 |
移动语义 | 不支持 | std::move() |
多态的核心价值
无论使用哪个C++版本,多态的核心价值都相同:
1.统一接口:
// 同一行代码处理不同类型
book->displayInfo(); // PhysicalBook显示位置,EBook显示格式
2.运行时决定
// 编译时不知道具体类型,运行时动态绑定
std::vector<Book*> mixed_books = {physical_book, e_book, magazine};
for (auto book : mixed_books) {book->borrow(); // 每种书的借阅方式不同
}
3.扩展性
// 添加新书类型无需修改现有代码
class AudioBook : public Book {void displayInfo() override { /* 音频书特有显示 */ }
};
// 现有的LibraryManager代码无需修改即可处理AudioBook