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自動釋放
    空指針NULLnullptr
    類型推導顯式聲明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

      本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
      如若轉載,請注明出處:http://www.pswp.cn/pingmian/91242.shtml
      繁體地址,請注明出處:http://hk.pswp.cn/pingmian/91242.shtml
      英文地址,請注明出處:http://en.pswp.cn/pingmian/91242.shtml

      如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

      相關文章

      mac操作筆記

      mac的操作筆記opt文件夾是干什么的&#xff1f;如何在某個訪達的文件夾里快速打開終端opt文件夾是干什么的&#xff1f; 在 macOS 中&#xff0c;/opt 目錄是一個可選&#xff08;optional&#xff09;軟件安裝目錄&#xff0c;主要用于存放第三方或非系統原生的應用程序。 /…

      紅黑樹×協程×內存序:2025 C++后端核心三體問題攻防手冊

      以下是2025年C后端開發全新高頻壓軸面試題&#xff0c;結合騰訊、字節、阿里等大廠最新技術棧&#xff0c;聚焦紅黑樹工程實踐、C20協程底層、Linux內核同步、分布式鎖實現及內存序重排五大核心領域&#xff0c;附工業級解決方案和手撕代碼示例&#xff1a; &#x1f333; 一、…

      《人工智能導論》(python版)第2章 python基礎2.2編程基礎

      書寫這篇博客的目的在于實踐并記錄《人工智能導論》&#xff08;Pyhton版&#xff09;微課視頻版這本書的內容&#xff0c;便于對人工智能有更深層次的理解。 參考文獻&#xff1a;姜春茂.人工智能導論&#xff08;Python版&#xff09;微課視頻版[M]. 北京:清華大學出版社,20…

      高可用部署

      一.keeplivaer nginx 高可用部署 下面為你詳細介紹基于 Keepalived 和 Nginx 在兩臺機器&#xff08;192.168.137.132 和 192.168.137.61&#xff09;上實現高可用部署的完整步驟&#xff1a; 一、環境準備&#xff08;兩臺服務器均執行&#xff09;環境準備 &#xff08;1&…

      java面向對象高級02——單例類(設計模式)

      1.什么是設計模式&#xff1f;一個問題可以有多種解法&#xff0c;在眾多解法的最優解法、方案就是設計模式。我們關注的點&#xff1a;某一種設計模式解決的是啥問題&#xff1f;這一設計模式怎么寫&#xff1f;2.單例設計模式a.作用單例設計模式的核心作用是確保一個類只有一…

      0730 數據結構重點整理

      Part 1.梳理數據結構重點一.宏1.簡單宏a. #define 宏名 宏體b. #if 宏(#ifndef)c.#endif2.多語句宏a. define 宏函數名(參數1&#xff0c;參數2......)({C語句1&#xff0c;C語句2......})b. define 宏函數名(參數1&#xff0c;參數2......)do(C語句1&#xff0c;C語句2......)…

      免費版酒店押金原路退回系統之【房費押金計算器】實踐——仙盟創夢IDE

      代碼<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>未來之窗——費用計算器</title><s…

      Windows下基于 SenseVoice模型的本地語音轉文字工具

      Windows下基于 SenseVoice模型的本地語音轉文字工具 前言&#xff1a; ? 現在很流行Vibe Coding但是指揮大模型寫代碼其實也是一件非常累的事情&#xff0c;經常需要輸入大段的文字去描述問題的現象以及具體的解決方案。剛好看到有一些博主通過本地部署語音大模型實現了語音轉…

      OWSM v4 語音識別學習筆記

      目錄 OWSM v4 簡介 卡內基梅隆大學 這個代碼不知道干嘛的 tokenizer CTC分割算法 yodas2數據集 依賴性安裝&#xff1a; 數據集下載地址&#xff1b; 模型下載地址&#xff1a; docker安裝&#xff08;適用于多數 Linux 系統&#xff09;測試ok 推理demo OWSM v4 簡介…

      機器學習線性回歸:從基礎到實踐的入門指南

      目錄 一、線性回歸的基本概念 二、線性回歸的核心原理 三、線性回歸的實現步驟 1.數據準備與預處理 2.模型訓練 3.模型評估 &#xff08;四&#xff09;模型優化與應用 四、線性回歸的應用場景 五、線性回歸的進階方向 在機器學習的廣闊領域中&#xff0c;線性回歸是入…

      6.Linux 系統啟動過程,破解root密碼與故障修復

      Linux :系統啟動過程&#xff0c;破解root密碼與故障修復 一、標準啟動流程 開機自檢 (BIOS/UEFI POST) 硬件初始化與檢測 MBR引導 讀取硬盤主引導記錄&#xff08;512字節&#xff09; GRUB2菜單 加載 /boot/grub2/grub.cfg 顯示啟動菜單 加載Linux內核 載入Linux 內核文件 內…

      特產|基于SSM+vue的南陽特產銷售平臺(源碼+數據庫+文檔)

      南陽特產銷售平臺 基于SSMvue的南陽特產銷售平臺 一、前言 二、系統設計 三、系統功能設計 平臺功能模塊 管理員功能模塊 商家功能模塊 四、數據庫設計 五、核心代碼 六、論文參考 七、最新計算機畢設選題推薦 八、源碼獲取&#xff1a; 博主介紹&#xff1a;??大…

      線性代數常見的解題方法

      一.行列式 1.利用行列式的性質進行簡化 (1)重要行列式 主對角線,副對角線(不要忘了-1的次數),拉普拉斯展開(副對角線是m*n),范德蒙 (2)行列式展開定理 每一行/列的元素乘以它對應的代數余子式 擴展:拉普拉斯展開定理,可以按照任意行和列數進行展開,行列式的值=|A|*…

      Websocket實時行情接口 (2025最新使用教程)

      本教程將指導您如何使用Java Websocket客戶端連接實時行情接口&#xff0c;并訂閱相關數據。 步驟1&#xff1a;配置您的項目 確保您的項目已引入以下依賴&#xff1a; jakarta.websocket-apijakarta.websocket-client-apifastjson2lombokspring-context (如果使用Spring框架) …

      【JEECG】JVxeTable表格拖拽排序功能

      功能說明&#xff1a; 實現JVxeTable表格拖拽排序功能 解決子表拖拽排序后&#xff0c;點擊保存數據&#xff0c;未實現拖拽排序后效果 參數配置&#xff1a; 提示&#xff1a; 1.開啟 dragSort 屬性之后即可實現上下拖拽排序。 2.使用 sortKey 屬性可以自定義排序保存的 key&…

      【騰訊云】EdgeOne網站安全防護的配置方法 防范盜刷流量 附惡意IP和UA黑名單

      經過上個月的前車之鑒&#xff0c;我摸索出一套針對騰訊云EdgeOne《付費版》的安全配置模板&#xff0c;僅供各位站長參考 配置方法 一、在EdgeOne控制面板頁面&#xff0c;點擊要配置的域名。 二、進入后&#xff0c;點擊安全防護-WEB防護-自定義規則&#xff0c;按圖所示添加…

      白玩 一 記錄retrofit+okhttp+flow 及 kts的全局配置

      先回憶下flow吧&#xff01; flow是啥 Flow 是 Kotlin 協程框架中的一個異步數據流處理組件&#xff0c;專為響應式編程設計&#xff0c;適用于需要連續或異步返回多個值的場景&#xff0c;如網絡請求、數據庫查詢、傳感器數據等 1 ?異步流&#xff08;Asynchronous Stream…

      犯罪現場三維還原:科技助力刑偵變革

      在刑偵領域&#xff0c;犯罪現場的準確還原對于案件偵破起著至關重要的作用。傳統的現場記錄方式&#xff0c;如拍照、繪圖等&#xff0c;雖然能獲取一定信息&#xff0c;但難以全面、直觀地呈現現場全貌&#xff0c;容易遺漏關鍵細節&#xff0c;且在后期分析和信息傳達上存在…

      go-admin 構建arm鏡像

      目錄 1、 go-admin Dockerfile 2、docker build go-admin 3、settings.yml 4、go-admin-ui Dockerfile 5、docker build go-admin-ui 6、go-admin.yaml 7、go-admin-ui.yaml 1、 go-admin Dockerfile # 構建階段:使用 Go 1.24 版本(支持遠程調試) FROM golang:1.24-…

      深入淺出:C++ STL簡介與學習指南

      目錄 前言 STL的版本演變 STL六大組件 STL的重要性 如何學習STL STL的缺陷 總結 前言 什么是STL&#xff1f; STL&#xff08;Standard Template Library&#xff0c;標準模板庫&#xff09;是C標準庫的核心組成部分&#xff0c;它不僅是一個可復用的組件庫&#xff0c;更是一…