#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;
}
核心成員: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;
}
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(); }
}
class Base { virtual ~Base(){} };
class Derived : public Base { public: void derivedOnly() {} };
void process(Base* ptr) {
if (Derived* d = dynamic_cast(ptr)) {
d->derivedOnly(); // 安全調用派生類特有功能
}
// 基類通用處理...
}
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";
}
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: 類型解讀
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: 靜態成員函數返回成員指針
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: 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 實現單詞查詢系統 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 封裝 |
| RTTI | dynamic_cast 指針版返回 nullptr,引用版拋 bad_cast;typeid 比較動態類型 |
| 成員指針 | 數據成員指針 &Class::member;成員函數指針 &Class::func;類型別名簡化 |
| 聯合體 | 手動管理活躍成員生命週期,placement new / 顯式析構 |
| 標準庫結合 | mem_fn、bind 將成員函數適配為可調用對象用於算法 |
第19章涵蓋 C++ 進階核心特性:動態內存、RTTI、成員指針、聯合體、類型別名,是通往 C++ 專家之路的必經之路。