目錄
面向對象編程(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