黑马程序员C++核心编程实战笔记:从内存模型到面向对象

📅 2026/7/15 4:40:45
黑马程序员C++核心编程实战笔记:从内存模型到面向对象
1. C内存分区模型深度解析第一次接触C内存模型时我盯着代码里变量的地址打印结果发愣——为什么局部变量的地址像坐过山车而全局变量的地址稳如泰山后来在调试一个崩溃程序时才明白这背后是C内存分区的精妙设计。1.1 四大内存区域详解想象你家的储物空间代码区像保险柜里的家规手册只读不可改全局区像客厅的展示柜生命周期最长栈区像临时放在玄关的快递随用随清堆区像可扩展的储物间自由但需自己打理实测这段代码能看到典型地址分布#include iostream using namespace std; int global_var; // 全局区 const int const_global 100; // 全局常量区 int main() { int stack_var; // 栈区 int* heap_var new int(10); // 堆区分配 cout 代码区地址 (void*)main endl; cout 全局变量地址 global_var endl; cout 全局常量地址 const_global endl; cout 栈区变量地址 stack_var endl; cout 堆区变量地址 heap_var endl; delete heap_var; // 必须手动释放 return 0; }1.2 栈区陷阱与堆区管理踩过最痛的坑是返回栈区局部变量的指针int* dangerous_func() { int val 42; // 栈区变量 return val; // 错误函数结束即销毁 }而堆区内存泄漏更隐蔽void memory_leak() { int* arr new int[100]; // 忘记delete[] arr; }建议使用智能指针管理堆内存#include memory auto safe_ptr make_sharedint(42); // 自动释放2. 面向对象编程实战技巧2.1 类设计的三重境界初学时常把类写成结构体函数class Beginner { public: int data; void print() { cout data; } };进阶后学会封装class Intermediate { private: string name; public: void setName(string n) { if(!n.empty()) name n; } };高手会考虑移动语义class Expert { vectorint big_data; public: Expert(vectorint data) : big_data(move(data)) {} };2.2 多态的实现原理虚函数表是理解多态的关键。通过这个示例可以看到动态绑定的本质class Animal { public: virtual void speak() { cout ??? endl; } }; class Dog : public Animal { public: void speak() override { cout Woof! endl; } }; void test_poly(Animal* a) { a-speak(); // 动态绑定 } int main() { Dog d; test_poly(d); // 输出Woof! }通过调试器查看对象内存会发现虚函数表指针的存在。当基类指针调用虚函数时实际是通过这个指针找到正确的函数实现。3. 内存与对象的结合实践3.1 对象在内存中的生命周期栈对象自动管理void stack_life() { MyClass obj; // 构造函数调用 // ... } // 析构函数自动调用堆对象需要手动管理void heap_life() { MyClass* obj new MyClass(); // ... delete obj; // 必须显式调用 }3.2 深拷贝与浅拷贝陷阱我曾因浅拷贝导致双重释放崩溃class Problem { int* data; public: Problem() { data new int[100]; } ~Problem() { delete[] data; } // 缺拷贝构造函数和赋值运算符 };正确做法是实现拷贝控制class Solution { int* data; size_t size; public: Solution(size_t s) : size(s), data(new int[s]) {} ~Solution() { delete[] data; } // 深拷贝 Solution(const Solution other) : size(other.size) { data new int[size]; copy(other.data, other.datasize, data); } // 赋值运算符 Solution operator(Solution other) { swap(data, other.data); swap(size, other.size); return *this; } };4. 现代C编程范式4.1 移动语义优化从拷贝到移动的性能飞跃class HeavyResource { int* big_data; public: // 移动构造函数 HeavyResource(HeavyResource other) noexcept { big_data other.big_data; other.big_data nullptr; } // 移动赋值 HeavyResource operator(HeavyResource other) noexcept { if(this ! other) { delete[] big_data; big_data other.big_data; other.big_data nullptr; } return *this; } };4.2 RAII资源管理用对象生命周期管理资源class FileHandle { FILE* file; public: explicit FileHandle(const char* name) : file(fopen(name, r)) { if(!file) throw runtime_error(文件打开失败); } ~FileHandle() { if(file) fclose(file); } // 使用移动语义处理所有权转移 FileHandle(FileHandle other) : file(other.file) { other.file nullptr; } // 禁用拷贝 FileHandle(const FileHandle) delete; FileHandle operator(const FileHandle) delete; };在实际项目中这些技术组合使用能显著提升代码的健壮性。记得第一次用RAII管理数据库连接时再也不需要担心连接泄漏问题那种安心感至今难忘。