如何养成良好的C++编程习惯(一)—— 内存管理
开篇导读
“养成良好的编程习惯”其实是相当综合的一个命题,可以从多个角度、维度和层次进行论述和评判。如代码的风格、效率和可读性;模块设计的灵活性、可扩展性和耦合度等等。要试图把所有方面都阐述清楚必须花很多的精力,而且也不一定能阐述得全面。因此,本系列文章以软件开发的基础问题为切入点,阐述程序设计和代码编写方面的细节问题,以点带面,旨在激发大家的思考与总结,希望能为大家带来实际的帮助。
虽然本系列文章定位为科普读物,但本座相信它们不但适合新手们学习借鉴,同时也能引发老鸟们的反思与共鸣。欢迎大家提出宝贵的意见和反馈 ^_^
在开篇讲述本章主要内容之前,本座首先用小小篇幅论述一下一种良好的工作习惯 —— 积累、提炼与求精。在工作和学习的过程中,不断把学到的知识通过有效的方式积累起来,形成自己的知识库,随着知识量的扩大,就会得到从量变到质变的提升。另外还要不断地对知识进行提炼,随着自己知识面的扩大以及水平的提升,你肯定会发现原有知识库存在着一些片面、局限、笨拙甚至错误。这时,就需要你有精益求精精的态度和毅力对知识库进行优化整理。
也许以上这些各位都曾想过去实施,也明白其中的道理,但是自己就是给自己各种堂而皇之的借口不花时间去做。这样说吧,技术之路不好走,这个行业有两项基本要求:1、对软件开发工作本身有很大兴趣;2、耐得住寂寞。两者缺一不可,否则还是趁年轻早点转行吧,要不转做软件行业的销售、产品或者管理也行,总之就不要做开发 ^_^
________________________________________
内存管理相关问题
一提起 C/C++ 的内存管理,大部分人脑海里都立刻涌出 new / delete / malloc / free 等几个恐怖的单词吧?的确,C/C++ 的手工内存管理是它们区别于其他语言的一大特点,也像一道屏障立在那些想从其它语言转向 C/C++ 的人士身前。由此也引起各大论坛对 “C++ 人气低落”和“是否应该引入垃圾回收机制”等相关话题的剧烈争论。本座一直无视这些争论,其实并非本座不关心 C++ 的发展与命运,相反,本座十分关心。虽然从现在的眼光看来,无论是 C++ 身上有多少硬伤,C++ 委员会的大爷们和 C++ 编译器厂商的大佬们如何扯猫尾。毕竟最爱就是最爱,残缺美也是美,不解释。本座之所以不关心这些争论,原因是因为看透了,一门语言就像一种人生,是有生命周期的,没落只是快慢的问题,旧事物总会被新事物取代,这是客观规律不可避免。秦始皇最终不也是没找到长生不老的仙丹么?只要曾经发光发热过,在还有价值的时候能为大众所用就已经无憾了。本座在此还要申明一种态度:本座并不排斥任何语言,相反,本座对新语言的诞生非常感兴趣。会去了解它们的特点,看看它们能帮助解决哪方面的问题。正如这几年,由于工作需要,本座用得最多的是 Java 和一些动态语言(它们的确能解决很多问题),而 C/C++ 却没再用了。
嗯,扯远了,我们还是回到正题吧。说起 C/C++ 的内存管理似乎令人望而生畏,满屏的 new / delete / malloc / free,OutPut 窗口无尽的 Memory Leak 警告,程序诡异的 0X00000004 指针异常,仿佛回到那一年我们一起哭过的日子,你 Hold 得住吗?其实,现实并没有你想的那么糟糕。只要你付出一点点,花一点点心思,没错!就一点点而已 —— 用 C++ 类封装内存访问,就会解决你大部分的烦恼,让你受益终身。以 Windows 程序为例,主要有以下几种内存管理方式:
• 虚拟内存(Virtual Memory)
• 默认堆和私有堆(Process Heap & Private Heap)
• 内存映射文件(File Mapping)
• 进程堆栈(Heap,其实就是用 malloc() 或 默认的 new 操作符在 Process Heap 里一小块一小块地割肉 ^_^)
• 栈(Stack,内存由调用者或被调用者自动管理)
今天我们的主题是封装,至于每种内存模型的概念和 API 的使用方式在这里就不讲了,Google 一下就知道。其实用 C++ 封装上述前 4 种内存访问的原理都差不多,就是在构造函数或其他操作函数中分配内存,然后再在析构函数中确保内存被正确释放。虚拟内存、默认堆和私有堆的操作方式相似,这里就不一一展示了,有兴趣的朋友可以参考本座前几天发表的那篇无人问津的文章:《C++ 封装私有堆(Private Heap)》,哎!下面对内存映射文件的封装也只稍作介绍、我们主要讨论的是使用频率最高的 malloc() 和 new 的封装。
________________________________________
内存映射文件
下面的代码把 File Mapping 句柄以及从 File Mapping 映射出来的内存分别封装到 CFileMapping 和 CShareMemory 中,可以直接使用 CShareMemory 可以创建一个 File Mapping 以及映射 File Mapping 的内存。
class CFileMapping
{
public:
CFileMapping(
LPCTSTR lpszName,
DWORD dwMaximumSizeLow,
DWORD dwMaximumSizeHigh = 0,
HANDLE hFile = INVALID_HANDLE_VALUE,
DWORD flProtect = PAGE_READWRITE,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes = NULL
)
{
m_hMap = ::CreateFileMapping (
hFile,
lpFileMappingAttributes,
flProtect,
dwMaximumSizeHigh,
dwMaximumSizeLow,
lpszName
);
ASSERT(IsValid());
}
~CFileMapping()
{
 
补充:软件开发 , C++ ,