首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ Primer 第5版 第19章 練習題完整解析與代碼實現

     編輯:關於C++

19.1-19.2: 運算符 new/delete 重載與 StrVec 實現

19.1 全局 new/delete 重載追蹤

#include <iostream> <cstdlib>
void* operator new(size_t n) {
    cout << "new(size_t)\n";
    if (void* p = malloc(n)) return p;
    throw bad_alloc();
}
void operator delete(void* p) noexcept {
    cout << "delete(void*)\n";
    free(p);
}
int main() {
    int* a = new int(486);
    cout << a << ' ' << *a << endl;
    delete a;
}

19.2 StrVec - 簡化版 std::vector<string> 完整實現

核心成員:allocator<string> alloc, elements/first_free/cap 三指針管理內存。

class StrVec {
    allocator<string> alloc;
    string *elements, *first_free, *cap;
    
    void chk_n_alloc() { if (size() == capacity()) reallocate(); }
    pair<string*, string*> alloc_n_copy(const string*, const string*);
    void free(); void reallocate();
public:
    StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {}
    StrVec(initializer_list<string>);
    StrVec(const StrVec&); StrVec(StrVec&&) noexcept;
    StrVec& operator=(const StrVec&); StrVec& operator=(StrVec&&) noexcept;
    ~StrVec() { free(); }
    void push_back(const string&);
    size_t size() const { return first_free - elements; }
    size_t capacity() const { return cap - elements; }
    string* begin() const { return elements; }
    string* end() const { return first_free; }
};

// 關鍵函數實現
void StrVec::push_back(const string& s) {
    chk_n_alloc();
    alloc.construct(first_free++, s);
}

pair<string*, string*> StrVec::alloc_n_copy(const string* b, const string* e) {
    auto data = alloc.allocate(e - b);
    return {data, uninitialized_copy(b, e, data)};
}

void StrVec::free() {
    if (elements) {
        for (auto p = first_free; p != elements;) alloc.destroy(--p);
        alloc.deallocate(elements, cap - elements);
    }
}

void StrVec::reallocate() {
    auto newcap = size() ? 2*size() : 1;
    auto newdata = alloc.allocate(newcap);
    auto dest = newdata;
    for (auto p = elements; p != first_free; ++p)
        alloc.construct(dest++, std::move(*p)); // 移動構造避免拷貝
    free();
    elements = newdata;
    first_free = dest;
    cap = elements + newcap;
}

// 移動構造/賦值
StrVec::StrVec(StrVec&& s) noexcept 
    : elements(s.elements), first_free(s.first_free), cap(s.cap) {
    s.elements = s.first_free = s.cap = nullptr;
}
StrVec& StrVec::operator=(StrVec&& rhs) noexcept {
    if (this != &rhs) {
        free();
        elements = rhs.elements; first_free = rhs.first_free; cap = rhs.cap;
        rhs.elements = rhs.first_free = rhs.cap = nullptr;
    }
    return *this;
}

19.3-19.4: dynamic_cast 與 type_id 實戰

class A { virtual ~A(){} };
class B : public A { virtual ~B(){} };
class C : public B { virtual ~C(){} };
class D : public B, public A { virtual ~D(){} }; // 多重繼承

int main() {
    A* pa = new C;
    // a) B* pb = dynamic_cast(pa); // 成功 (C 是 B)
    // b) C* pc = dynamic_cast(new B); // 失敗 (B 不是 C) -> 返回 nullptr
    // c) B* pb2 = dynamic_cast(new D); // 成功 (D 繼承 B)
    
    try {
        C& rc = dynamic_cast(*pa);     // 成功
        C& rc2 = dynamic_cast(*new D); // 拋出 bad_cast (D 不是 C)
    } catch (bad_cast& e) { cout << e.what(); }
}

19.5: dynamic_cast 典型應用場景

class Base { virtual ~Base(){} };
class Derived : public Base { public: void derivedOnly() {} };

void process(Base* ptr) {
    if (Derived* d = dynamic_cast(ptr)) {
        d->derivedOnly(); // 安全調用派生類特有功能
    }
    // 基類通用處理...
}

19.6-19.8: RTTI 綜合測試

void cast_test() {
    QueryBase* pb1 = new AndQuery(...);
    QueryBase* pb2 = new OrQuery(...);
    
    // dynamic_cast 指針版
    if (dynamic_cast(pb1)) cout << "pb1 is AndQuery";
    if (dynamic_cast(pb2)) cout << "pb2 is AndQuery"; // 失敗
    
    // dynamic_cast 引用版 (需 try-catch)
    try { AndQuery& r = dynamic_cast(*pb1); }
    catch (bad_cast&) { /* pb1 不是 AndQuery */ }
    
    // typeid 比較動態類型
    if (typeid(*pb1) == typeid(*pb2)) cout << "same dynamic type";
    if (typeid(*pb1) == typeid(AndQuery)) cout << "pb1 is AndQuery";
}

19.9: type_info 輸出自定義

ostream& operator<<(ostream& os, const type_info& t) {
    if (t == typeid(int)) return os << "int";
    if (t == typeid(int[10])) return os << "int[10]";
    if (t == typeid(string)) return os << "string";
    if (t == typeid(Base)) return os << "Base";
    if (t == typeid(Base*)) return os << "Base*";
    if (t == typeid(Derived)) return os << "Derived";
    return os << t.name(); // 默認實現
}

19.10-19.12: 成員指針詳解

// 19.10: 類型解讀
class A*        // 指針類型
class A*        // 引用類型 (指向 A 的引用)
class B         // 引用指向的類型

// 19.11: 區別
// 普通指針:指向對象地址
// 成員指針:指向類成員偏移量 (非對象實例)

// 19.12: 成員指針獲取偏移
class Screen {
    string::size_type cursor, height, width;
    string contents;
public:
    static const string::size_type Screen::* data() { return &Screen::cursor; }
};
// 使用:Screen s; s.*Screen::data() 訪問 cursor

19.13-19.16: 成員函數指針與類型別名

// 19.13: 靜態成員函數返回成員指針
class Sales_data {
    string bookNo;
public:
    static const string Sales_data::* data() { return &Sales_data::bookNo; }
};

// 19.14: auto 推導成員函數指針
auto pmf = &Screen::get_cursor; // 類型:char (Screen::*)() const
// Screen::get 有重載,但有無參版本匹配

// 19.15: 函數指針 vs 成員函數指針
// 函數指針:返回類型 (*name)(參數列表)
// 成員函數指針:返回類型 (類名::*name)(參數列表) cv限定
// 賦值需 &Class::member

// 19.16: 類內類型別名簡化聲明
class Sales_data {
public:
    using Avg = double (Sales_data::*)() const; // 成員函數指針類型別名
    friend void test(Sales_data&);
};
void test(Sales_data& s) {
    Avg fn = &Sales_data::avg_price;
    cout << (s.*fn)(); // 調用
}

19.17-19.19: 成員指針數組與標準庫算法結合

// 19.17: Screen 公開成員函數指針類型
using Action_c_v = char (Screen::*)() const;        // get/get_cursor
using Action_c_uu = char (Screen::*)(pos,pos) const; // get(pos,pos)
using Action_Screen_uu = Screen& (Screen::*)(pos,pos); // move

// 19.18: mem_fn / bind 配合算法
vector vec = {"", "c++", "primer", "", "world"};
int cnt = count_if(vec.begin(), vec.end(), mem_fn(&string::empty));
// 或 bind
int cnt = count_if(vec.begin(), vec.end(), bind(&string::empty, _1));

// 19.19: 成員函數指針作一元謂詞
vector books = { {"b1",13,36.4}, {"b2",32,24.2}, ... };
auto it = find_if(books.begin(), books.end(), 
    bind(&Sales_data::avg_price, _1) > 50.0); // C++14 泛型 lambda 更簡潔
// 書中 bind 寫法需配合 lambda 捕獲比較值

19.20-19.25: TextQuery 系統與 Token 聯合體

19.20 實現單詞查詢系統 TextQuery/QueryResult,使用 map> 存儲倒排索引。

19.21-19.23 Token 類使用聯合體存儲多種類型值,需手動管理構造/析構:

class Token {
    enum { INT, CHAR, DBL, STR, SALES } tok;
    union { char cval; int ival; double dval; string sval; Sales_data sdval; };
    // 複製/移動/賦值/析構需按 tok 狀態手動處理聯合體成員
    // 如:tok==STR 時 placement new 構造 string,析構時顯式調用 ~string()
};

19.24 自賦值安全:成員有正確拷貝賦值運算符時自賦值無害。

19.25 C 語言鏈接規格 extern "C" 不支持函數重載 (無名字修飾)。

核心知識點總結

主題關鍵點
內存管理allocator 分離分配/構造,移動語義優化 reallocate,RAII 封裝
RTTIdynamic_cast 指針版返回 nullptr,引用版拋 bad_cast;typeid 比較動態類型
成員指針數據成員指針 &Class::member;成員函數指針 &Class::func;類型別名簡化
聯合體手動管理活躍成員生命週期,placement new / 顯式析構
標準庫結合mem_fn、bind 將成員函數適配為可調用對象用於算法

第19章涵蓋 C++ 進階核心特性:動態內存、RTTI、成員指針、聯合體、類型別名,是通往 C++ 專家之路的必經之路。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved