C++ Primer 第19章:特殊工具与技术
19.1 控制内存分配
19.1.1 重载 new 和 delete
// overload_new_delete.cpp -- 重载new和delete #include <iostream> #include <cstdlib> #include <cstring> #include <stdexcept> // ===== 全局 new/delete 重载 ===== // 通常不建议重载全局版本,会影响所有内存分配 // ===== 类级别的 new/delete 重载 ===== class MyClass { public: MyClass(int v = 0) : val_(v) { std::cout << "[构造] val=" << val_ << std::endl; } ~MyClass() { std::cout << "[析构] val=" << val_ << std::endl; } // 重载 operator new:分配内存 static void* operator new(std::size_t size) { std::cout << "[new] 分配 " << size << " 字节" << std::endl; void* p = std::malloc(size); if (!p) throw std::bad_alloc(); return p; } // 重载 operator delete:释放内存 static void operator delete(void* p, std::size_t size) noexcept { std::cout << "[delete] 释放 " << size << " 字节" << std::endl; std::free(p); } // 重载数组版本 static void* operator new[](std::size_t size) { std::cout << "[new[]] 分配 " << size << " 字节" << std::endl; void* p = std::malloc(size); if (!p) throw std::bad_alloc(); return p; } static void operator delete[](void* p, std::size_t size) noexcept { std::cout << "[delete[]] 释放 " << size << " 字节" << std::endl; std::free(p); } int val() const { return val_; } private: int val_; }; int main() { using namespace std; cout << "===== 单个对象 =====" << endl; MyClass* p = new MyClass(42); // 调用 operator new,然后构造函数 cout << "val = " << p->val() << endl; delete p; // 调用析构函数,然后 operator delete cout << "\n===== 对象数组 =====" << endl; MyClass* arr = new MyClass[3]; // 调用 operator new[] delete[] arr; // 调用 operator delete[] return 0; }
19.1.2 定位 new(placement new)
// placement_new.cpp -- 定位new #include <iostream> #include <new> #include <string> class Widget { public: Widget(const std::string& name) : name_(name) { std::cout << "[构造] " << name_ << std::endl; } ~Widget() { std::cout << "[析构] " << name_ << std::endl; } void show() const { std::cout << "Widget: " << name_ << std::endl; } private: std::string name_; }; int main() { using namespace std; // ===== 定位new:在指定内存上构造对象 ===== // 语法:new (地址) 类型(参数) // 在栈上分配内存 alignas(Widget) char buffer[sizeof(Widget)]; // 在 buffer 上构造 Widget(不分配内存) Widget* p = new (buffer) Widget("栈上的Widget"); p->show(); // 必须手动调用析构函数!(不能用 delete) p->~Widget(); // ===== 在堆上预分配内存,然后构造 ===== void* raw = ::operator new(sizeof(Widget)); // 只分配内存 Widget* p2 = new (raw) Widget("堆上的Widget"); p2->show(); p2->~Widget(); // 手动析构 ::operator delete(raw); // 手动释放内存 // ===== 对象池示例 ===== cout << "\n===== 对象池 =====" << endl; const int POOL_SIZE = 3; alignas(Widget) char pool[sizeof(Widget) * POOL_SIZE]; // 在池中构造多个对象 Widget* widgets[POOL_SIZE]; for (int i = 0; i < POOL_SIZE; i++) { widgets[i] = new (pool + i * sizeof(Widget)) Widget("池对象" + to_string(i)); } for (int i = 0; i < POOL_SIZE; i++) widgets[i]->show(); // 手动析构(逆序) for (int i = POOL_SIZE - 1; i >= 0; i--) widgets[i]->~Widget(); return 0; }
19.2 运行时类型识别(RTTI)
19.2.1 dynamic_cast
// dynamic_cast_demo.cpp -- dynamic_cast详解 #include <iostream> #include <memory> #include <string> class Shape { public: virtual double area() const = 0; virtual std::string name() const = 0; virtual ~Shape() {} }; class Circle : public Shape { public: Circle(double r) : radius_(r) {} double area() const override { return 3.14159 * radius_ * radius_; } std::string name() const override { return "Circle"; } double radius() const { return radius_; } private: double radius_; }; class Rectangle : public Shape { public: Rectangle(double w, double h) : w_(w), h_(h) {} double area() const override { return w_ * h_; } std::string name() const override { return "Rectangle"; } double width() const { return w_; } double height() const { return h_; } private: double w_, h_; }; int main() { using namespace std; Shape* shapes[] = { new Circle(5.0), new Rectangle(4.0, 6.0), new Circle(3.0) }; cout << "===== dynamic_cast 指针版本 =====" << endl; for (auto s : shapes) { cout << s->name() << " 面积=" << s->area() << endl; // 向下转型:成功返回有效指针,失败返回 nullptr if (Circle* c = dynamic_cast<Circle*>(s)) { cout << " 半径=" << c->radius() << endl; } else if (Rectangle* r = dynamic_cast<Rectangle*>(s)) { cout << " 宽=" << r->width() << " 高=" << r->height() << endl; } } cout << "\n===== dynamic_cast 引用版本 =====" << endl; // 引用版本:失败时抛出 std::bad_cast try { Shape& s = *shapes[0]; // Circle Circle& c = dynamic_cast<Circle&>(s); // 成功 cout << "Circle半径:" << c.radius() << endl; Shape& s2 = *shapes[1]; // Rectangle Circle& c2 = dynamic_cast<Circle&>(s2); // 失败!抛出 bad_cast } catch (const bad_cast& e) { cout << "bad_cast:" << e.what() << endl; } for (auto s : shapes) delete s; return 0; }
19.2.2 typeid 运算符
// typeid_demo.cpp -- typeid运算符 #include <iostream> #include <typeinfo> #include <string> class Base { public: virtual ~Base() {} virtual std::string type() const { return "Base"; } }; class Derived : public Base { public: std::string type() const override { return "Derived"; } }; int main() { using namespace std; // ===== typeid 基本用法 ===== int i = 42; double d = 3.14; string s = "Hello"; // typeid 返回 type_info 对象的引用 cout << "int: " << typeid(i).name() << endl; cout << "double: " << typeid(d).name() << endl; cout << "string: " << typeid(s).name() << endl; // ===== typeid 与多态 ===== cout << "\n===== 多态类型 =====" << endl; Base b; Derived d2; Base* p1 = &b; Base* p2 = &d2; // 对指针使用 typeid:返回指针类型(不是所指对象的类型) cout << "typeid(p1): " << typeid(p1).name() << endl; // Base* // 对解引用指针使用 typeid:返回实际对象类型(动态类型) cout << "typeid(*p1): " << typeid(*p1).name() << endl; // Base cout << "typeid(*p2): " << typeid(*p2).name() << endl; // Derived // ===== 类型比较 ===== cout << "\n===== 类型比较 =====" << endl; cout << boolalpha; cout << "p1指向Base:" << (typeid(*p1) == typeid(Base)) << endl; cout << "p2指向Derived:" << (typeid(*p2) == typeid(Derived)) << endl; cout << "p1和p2类型相同:" << (typeid(*p1) == typeid(*p2)) << endl; // ===== 实际应用:类型安全的操作 ===== cout << "\n===== 类型安全操作 =====" << endl; Base* shapes[] = {new Base(), new Derived(), new Base()}; for (auto s : shapes) { if (typeid(*s) == typeid(Derived)) cout << "这是Derived对象" << endl; else cout << "这是Base对象" << endl; delete s; } return 0; }
19.3 枚举类型
// enum_types.cpp -- 枚举类型 #include <iostream> #include <string> // ===== 传统枚举(不限定作用域)===== enum Color { RED, GREEN, BLUE }; // RED=0, GREEN=1, BLUE=2 enum Status { ACTIVE, INACTIVE }; // ⚠️ 传统枚举的问题: // 1. 枚举值泄漏到外层作用域 // 2. 不同枚举类型可以比较(隐式转换为int) // 3. 不能指定底层类型(默认int) // ===== 限定作用域的枚举(C++11)===== enum class Direction { NORTH, SOUTH, EAST, WEST }; enum class Permission : uint8_t // 指定底层类型 { NONE = 0, READ = 1, WRITE = 2, EXECUTE = 4, ALL = READ | WRITE | EXECUTE }; // ===== 枚举的前置声明(C++11)===== enum class Season : int; // 前置声明 void printSeason(Season s); enum class Season : int { SPRING, SUMMER, AUTUMN, WINTER }; void printSeason(Season s) { switch (s) { case Season::SPRING: std::cout << "春天" << std::endl; break; case Season::SUMMER: std::cout << "夏天" << std::endl; break; case Season::AUTUMN: std::cout << "秋天" << std::endl; break; case Season::WINTER: std::cout << "冬天" << std::endl; break; } } int main() { using namespace std; // 传统枚举 Color c = RED; int n = c; // ✅ 隐式转换为int // Color c2 = 0; // ❌ 不能从int转换 // 不同枚举类型可以比较(危险!) // if (RED == ACTIVE) {} // 编译通过,但语义错误 // 限定作用域枚举 Direction d = Direction::NORTH; // int m = d; // ❌ 不能隐式转换 int m = static_cast<int>(d); // ✅ 必须显式转换 // 不同枚举类型不能比较 // if (Direction::NORTH == Season::SPRING) {} // ❌ 编译错误 // 权限枚举 Permission perm = Permission::READ; cout << "READ = " << static_cast<int>(perm) << endl; // 使用位运算(需要显式转换) auto combined = static_cast<Permission>( static_cast<uint8_t>(Permission::READ) | static_cast<uint8_t>(Permission::WRITE)); cout << "READ|WRITE = " << static_cast<int>(combined) << endl; // 季节枚举 printSeason(Season::AUTUMN); // 枚举的大小 cout << "\n枚举大小:" << endl; cout << "Color: " << sizeof(Color) << " 字节" << endl; cout << "Permission: " << sizeof(Permission) << " 字节" << endl; return 0; }
19.4 类成员指针
// member_pointer.cpp -- 类成员指针 #include <iostream> #include <string> #include <vector> #include <algorithm> #include <functional> class Screen { public: using Action = Screen& (Screen::*)(); // 成员函数指针类型别名 Screen(int h, int w, char c) : height_(h), width_(w), contents_(h * w, c), cursor_(0) {} // 成员函数 Screen& home() { cursor_ = 0; return *this; } Screen& end() { cursor_ = contents_.size() - 1; return *this; } Screen& forward() { if (cursor_ < contents_.size() - 1) ++cursor_; return *this; } Screen& back() { if (cursor_ > 0) --cursor_; return *this; } char get() const { return contents_[cursor_]; } int pos() const { return cursor_; } // 数据成员 int height_, width_; std::string contents_; int cursor_; }; int main() { using namespace std; Screen s(3, 5, 'X'); // ===== 数据成员指针 ===== // 声明:类型 类名::*指针名 int Screen::*pHeight = &Screen::height_; int Screen::*pWidth = &Screen::width_; // 通过对象使用成员指针 cout << "height = " << s.*pHeight << endl; // 3 cout << "width = " << s.*pWidth << endl; // 5 // 通过指针使用成员指针 Screen* ps = &s; cout << "height = " << ps->*pHeight << endl; // ===== 成员函数指针 ===== // 声明:返回类型 (类名::*指针名)(参数类型) Screen& (Screen::*pForward)() = &Screen::forward; Screen& (Screen::*pBack)() = &Screen::back; // 通过对象调用 (s.*pForward)(); cout << "前进后位置:" << s.pos() << endl; (s.*pBack)(); cout << "后退后位置:" << s.pos() << endl; // 通过指针调用 (ps->*pForward)(); cout << "通过指针前进:" << s.pos() << endl; // ===== 成员函数指针数组 ===== Screen::Action actions[] = { &Screen::home, &Screen::forward, &Screen::end, &Screen::back }; // 执行所有动作 for (auto action : actions) (s.*action)(); // ===== 使用 std::mem_fn 和 std::bind ===== cout << "\n===== mem_fn 和 bind =====" << endl; // mem_fn:将成员函数包装为可调用对象 auto getPos = mem_fn(&Screen::pos); cout << "pos = " << getPos(s) << endl; // bind:绑定成员函数 auto forward = bind(&Screen::forward, &s); forward(); cout << "bind forward后:" << s.pos() << endl; return 0; }
19.5 嵌套类
// nested_class.cpp -- 嵌套类 #include <iostream> #include <string> #include <memory> // 嵌套类:定义在另一个类内部的类 class TextQuery { public: // 嵌套类:QueryResult class QueryResult { friend class TextQuery; public: QueryResult(const std::string& word, int count) : word_(word), count_(count) {} void print() const { std::cout << "\"" << word_ << "\" 出现 " << count_ << " 次" << std::endl; } int count() const { return count_; } private: std::string word_; int count_; }; // TextQuery 的成员 void addWord(const std::string& word) { wordCount_[word]++; } QueryResult query(const std::string& word) const { auto it = wordCount_.find(word); int count = (it != wordCount_.end()) ? it->second : 0; return QueryResult(word, count); } private: std::map<std::string, int> wordCount_; }; // 在类外定义嵌套类的成员 // TextQuery::QueryResult::QueryResult(...) { ... } int main() { using namespace std; TextQuery tq; string text = "the quick brown fox jumps over the lazy dog the"; // 分词并统计 istringstream iss(text); string word; while (iss >> word) tq.addWord(word); // 查询 auto result1 = tq.query("the"); auto result2 = tq.query("fox"); auto result3 = tq.query("cat"); result1.print(); // "the" 出现 3 次 result2.print(); // "fox" 出现 1 次 result3.print(); // "cat" 出现 0 次 return 0; }
19.6 union:一种节省空间的类
// union_demo.cpp -- union #include <iostream> #include <string> #include <new> // ===== 基本 union ===== union Data { int ival; double dval; char cval; }; // 大小等于最大成员的大小 // ===== 匿名 union ===== struct Token { enum class TokenKind { INT, DOUBLE, STRING }; TokenKind kind; union // 匿名union:成员直接在外层作用域可见 { int ival; double dval; // std::string sval; // ⚠️ 含有构造函数的类型需要特殊处理 }; }; // ===== 含有类类型成员的 union ===== class MyUnion { public: enum class Type { INT, STRING, NONE }; MyUnion() : type_(Type::NONE) {} MyUnion(int v) : type_(Type::INT) { new (&ival_) int(v); // 定位new } MyUnion(const std::string& s) : type_(Type::STRING) { new (&sval_) std::string(s); // 定位new } MyUnion(const MyUnion& other) : type_(other.type_) { switch (type_) { case Type::INT: new (&ival_) int(other.ival_); break; case Type::STRING: new (&sval_) std::string(other.sval_); break; default: break; } } ~MyUnion() { if (type_ == Type::STRING) sval_.~basic_string<char>(); // 手动析构 } void show() const { switch (type_) { case Type::INT: std::cout << "int: " << ival_ << std::endl; break; case Type::STRING: std::cout << "string: " << sval_ << std::endl; break; default: std::cout << "none" << std::endl; } } private: Type type_; union { int ival_; std::string sval_; }; }; int main() { using namespace std; // 基本 union Data d; d.ival = 42; cout << "ival = " << d.ival << endl; d.dval = 3.14; // 覆盖了 ival cout << "dval = " << d.dval << endl; // d.ival 现在是未定义的 cout << "union大小:" << sizeof(Data) << " 字节" << endl; // 含类类型的 union MyUnion u1(42); MyUnion u2(string("Hello")); MyUnion u3; u1.show(); u2.show(); u3.show(); return 0; }
19.7 局部类
// local_class.cpp -- 局部类 #include <iostream> #include <string> #include <functional> // 局部类:定义在函数内部的类 std::function<int(int)> makeAdder(int n) { // 局部类 class Adder { public: Adder(int n) : n_(n) {} int operator()(int x) const { return x + n_; } private: int n_; // ⚠️ 局部类的限制: // 1. 不能定义静态数据成员 // 2. 不能访问外层函数的局部变量(除非是静态的) // 3. 可以访问外层函数的类型名、静态变量、枚举值 }; return Adder(n); } // 局部类在模板中的应用 template <typename T> std::function<bool(T)> makeGreaterThan(T threshold) { class Comparator { public: Comparator(T t) : threshold_(t) {} bool operator()(T val) const { return val > threshold_; } private: T threshold_; }; return Comparator(threshold); } int main() { using namespace std; auto add5 = makeAdder(5); auto add10 = makeAdder(10); cout << "add5(3) = " << add5(3) << endl; // 8 cout << "add10(3) = " << add10(3) << endl; // 13 auto gt5 = makeGreaterThan(5); auto gt10 = makeGreaterThan(10.0); cout << boolalpha; cout << "7 > 5: " << gt5(7) << endl; // true cout << "3 > 5: " << gt5(3) << endl; // false cout << "15.0 > 10.0: " << gt10(15.0) << endl; // true return 0; }
19.8 固有的不可移植的特性
19.8.1 位域
// bit_fields.cpp -- 位域 #include <iostream> // 位域:允许将多个标志位打包到一个整数中 struct NetworkPacket { unsigned int version : 4; // 4位:版本号(0-15) unsigned int type : 4; // 4位:类型(0-15) unsigned int flags : 8; // 8位:标志位 unsigned int length : 16; // 16位:长度(0-65535) // 总共 32 位 = 4 字节 }; struct Permissions { unsigned int read : 1; // 1位 unsigned int write : 1; // 1位 unsigned int execute : 1; // 1位 unsigned int : 5; // 5位填充(未命名) }; int main() { using namespace std; NetworkPacket pkt; pkt.version = 4; pkt.type = 2; pkt.flags = 0b00000001; pkt.length = 1024; cout << "版本:" << pkt.version << endl; cout << "类型:" << pkt.type << endl; cout << "标志:" << pkt.flags << endl; cout << "长度:" << pkt.length << endl; cout << "结构体大小:" << sizeof(NetworkPacket) << " 字节" << endl; Permissions perm = {1, 1, 0}; cout << "\n读权限:" << perm.read << endl; cout << "写权限:" << perm.write << endl; cout << "执行权限:" << perm.execute << endl; return 0; }
19.8.2 volatile 限定符
// volatile_demo.cpp -- volatile #include <iostream> // volatile:告诉编译器不要优化该变量 // 每次访问都从内存读取,不使用缓存 // 常见用途: // 1. 硬件寄存器 // 2. 多线程共享变量(但不能替代互斥锁) // 3. 信号处理程序中的变量 volatile int hardwareReg = 0; // 模拟硬件寄存器 // volatile 与 const 组合 volatile const int readOnlyReg = 0xFF; // 只读硬件寄存器 void readHardware() { // 每次都从内存读取,不会被优化为只读一次 for (int i = 0; i < 3; i++) std::cout << "寄存器值:" << hardwareReg << std::endl; } // volatile 成员函数 class HardwareDevice { public: void read() volatile { // 可以被 volatile 对象调用 std::cout << "读取设备" << std::endl; } void write() volatile { std::cout << "写入设备" << std::endl; } }; int main() { readHardware(); volatile HardwareDevice dev; dev.read(); dev.write(); return 0; }
19.8.3 链接指示(extern "C")
// extern_c.cpp -- 链接指示 #include <iostream> // extern "C":告诉编译器使用C语言的链接规则 // 主要用于与C代码互操作 // 单个函数 extern "C" void c_function(int x) { // 使用C链接,函数名不会被C++名字修饰 std::cout << "C函数:" << x << std::endl; } // 多个函数 extern "C" { void c_func1() { std::cout << "c_func1" << std::endl; } void c_func2() { std::cout << "c_func2" << std::endl; } int c_add(int a, int b) { return a + b; } } // 包含C头文件 // extern "C" // { // #include <stdio.h> // #include <string.h> // } // 在头文件中同时支持C和C++ #ifdef __cplusplus extern "C" { #endif void shared_function(int x) { std::cout << "共享函数:" << x << std::endl; } #ifdef __cplusplus } #endif int main() { c_function(42); c_func1(); c_func2(); std::cout << "c_add(3,4) = " << c_add(3, 4) << std::endl; shared_function(100); return 0; }
19.9 综合示例:内存池
// memory_pool.cpp -- 综合示例:内存池 #include <iostream> #include <vector> #include <string> #include <stdexcept> #include <new> #include <cstring> // 固定大小的内存池 template <typename T, size_t BlockSize = 64> class MemoryPool { private: // 内存块 struct Block { alignas(T) char data[sizeof(T)]; bool inUse = false; }; std::vector<Block> pool_; size_t allocated_ = 0; public: MemoryPool() : pool_(BlockSize) {} // 分配内存 T* allocate() { for (auto& block : pool_) { if (!block.inUse) { block.inUse = true; ++allocated_; return reinterpret_cast<T*>(block.data); } } throw std::bad_alloc(); } // 释放内存 void deallocate(T* p) noexcept { for (auto& block : pool_) { if (reinterpret_cast<T*>(block.data) == p) { block.inUse = false; --allocated_; return; } } } // 构造对象 template <typename... Args> T* construct(Args&&... args) { T* p = allocate(); try { new (p) T(std::forward<Args>(args)...); // 定位new } catch (...) { deallocate(p); throw; } return p; } // 析构并释放 void destroy(T* p) noexcept { p->~T(); deallocate(p); } size_t allocated() const { return allocated_; } size_t capacity() const { return pool_.size(); } size_t available() const { return capacity() - allocated(); } }; // 使用内存池的类 class Widget { public: Widget(const std::string& name, int val) : name_(name), val_(val) { std::cout << "[构造] " << name_ << "(" << val_ << ")" << std::endl; } ~Widget() { std::cout << "[析构] " << name_ << std::endl; } void show() const { std::cout << "Widget: " << name_ << " = " << val_ << std::endl; } private: std::string name_; int val_; }; int main() { using namespace std; MemoryPool<Widget, 5> pool; cout << "===== 内存池状态 =====" << endl; cout << "容量:" << pool.capacity() << endl; cout << "可用:" << pool.available() << endl; cout << "\n===== 分配对象 =====" << endl; Widget* w1 = pool.construct("Widget1", 100); Widget* w2 = pool.construct("Widget2", 200); Widget* w3 = pool.construct("Widget3", 300); cout << "已分配:" << pool.allocated() << endl; w1->show(); w2->show(); w3->show(); cout << "\n===== 释放对象 =====" << endl; pool.destroy(w2); cout << "释放w2后,已分配:" << pool.allocated() << endl; cout << "\n===== 复用内存 =====" << endl; Widget* w4 = pool.construct("Widget4", 400); // 复用w2的内存 w4->show(); cout << "\n===== 清理 =====" << endl; pool.destroy(w1); pool.destroy(w3); pool.destroy(w4); cout << "最终已分配:" << pool.allocated() << endl; return 0; }
📝 第19章知识点总结
| 知识点 | 核心要点 |
|---|
| 重载 new/delete | 类级别重载,operator new分配内存,operator delete释放 |
| 定位 new | new (地址) 类型(参数),在指定内存构造,必须手动调用析构函数 |
| dynamic_cast | 安全向下转型,指针版本失败返回nullptr,引用版本失败抛bad_cast |
| typeid | 返回type_info,对多态类型返回动态类型,对指针返回静态类型 |
| enum class | 限定作用域,不隐式转换为int,可指定底层类型,推荐使用 |
| 数据成员指针 | 类型 类名::*ptr = &类名::成员,通过.*或->*使用 |
| 成员函数指针 | 返回类型 (类名::*ptr)(参数),通过.*或->*调用 |
| mem_fn | 将成员函数包装为可调用对象 |
| 嵌套类 | 定义在类内部,有自己的作用域,外层类不能直接访问嵌套类的私有成员 |
| union | 所有成员共享内存,大小等于最大成员,含类类型成员需手动管理 |
| 局部类 | 定义在函数内部,不能有静态数据成员,不能访问外层局部变量 |
| 位域 | 指定成员占用的位数,节省内存,常用于硬件协议 |
| volatile | 防止编译器优化,每次从内存读取,用于硬件寄存器和信号处理 |
| extern "C" | 使用C链接规则,用于C/C++互操作,防止名字修饰 |