Winx基本类使用指南之C++标准扩展(AutoFreeAlloc)[注1] 在C++中,一般有两种内存分配的方式: 1. 传统的配对方式 C++中的new与delete C中alloc与free Windows API中GlobalAlloc与GlobalFree、HeapAlloc与HeapFree等 这种分配方式首先一件事就是要记住在分配内存后一定要释放,这一点在吃过几次亏后总是能够记住;但是在某个程序段的分支或异常处理多起来时(并且这个程序段不止一次的分配内存),再使用这种内存分配方式就会头大如斗(我想,绝大多数的C++程序员都应该对此有所体会)。 2. 使用智能指针 使用智能指针可以解决传统的配对方式的问题,但同时却又引出了新的问题。智能指针品种很多,记住它们的各种规则更是困难。本人愚苯,每次使用boost库中的智能指针,总是先要去看它们的帮助文档。 现在终于有了第三种内存分配方式,这就是Winx中AutoFreeAlloc机能(这个机能属于Winx的标准C++的扩展部分),其基本原理是由一个AutoFreeAlloc负责某程序段中所有的内存分配,而不管所分配内存是由什么对象使用,在该程序段结束时,再由这个AutoFreeAlloc释放所有被分配的内存。对于具体的底层实现机制,感兴趣的朋友可以参照xushiwei的最袖珍的垃圾回收器。 下面就转入正题,介绍一下AutoFreeAlloc的使用方法及在使用中要注意的一些问题。 使用AutoFreeAlloc的例子 下面的例子主要演示了使用AutoFreeAlloc的一些方式(在main函数中),其主要功能是首先定义一个分配器,然后为一系列对象分配内存。 class ClassA { private: int var_int_; double var_double_; public: ClassA() { } }; class ClassB { private: int var_int_; double var_double_; public: ClassB() : var_int_(0), var_double_(0.0) { } ClassB(const ClassB& cb) : var_int_(cb.var_int_), var_double_(cb.var_double_) {} ~ClassB() { } }; class ClassC { private: int var_int_; double var_double_; ClassB class_b_; public: ClassC() { } ~ClassC() { } }; int main() { AutoFreeAlloc alloc; ClassA* ca = STD_NEW(alloc, ClassA); ClassA* caArray = STD_NEW_ARRAY(alloc, ClassA, 100); for (int i = 0; i < 100; ++i) { // print address of each element of Array caArray cout << caArray + i << endl; } ClassB cbTemp; // assign cbTemp to cb ClassB* cb = STD_NEW(alloc, ClassB)(cbTemp); ClassB* cbArray = STD_NEW_ARRAY(alloc, ClassB, 100); ClassC* cc = STD_NEW(alloc, ClassC); ClassC* ccArray = STD_NEW_ARRAY(alloc, ClassC, 100); // assign 1 to i1 int* i1 = STD_NEW(alloc, int)(1); int* i2 = STD_NEW_ARRAY(alloc, int, 100); // assign 0.0 to d1 double* d1 = STD_NEW(alloc, double)(0.0); double* d2 = STD_NEW_ARRAY(alloc, double, 100); int* i3 = STD_ALLOC(alloc, int); int* i4 = STD_ALLOC_ARRAY(alloc, int, 100); double* d3 = STD_ALLOC(alloc, double); double* d4 = STD_ALLOC_ARRAY(alloc, double, 100); char* s1 = STD_NEW(alloc, char); *s1 = 'T'; cout << *s1 << endl; char* s2 = STD_NEW_ARRAY(alloc, char, 128); strcpy(s2, "hello world!"); cout << s2 << endl; char* s3 = STD_NEW(alloc, char); char* s4 = STD_NEW_ARRAY(alloc, char, 128); strcpy(s4, "hello world 2!"); cout << s4 << endl; return 0; } 通过上面的例子可以看出,AutoFreeAlloc的使用非常简单,只有2个步骤: 1. 定义AutoFreeAlloc 2. 通过STD_NEW、STD_NEW_ARRAY、STD_ALLOC、STD_ALLOC_ARRAY这4个宏中的一个来分配内存(或者说创建对象),这是不是有点象java中的new操作呢?J 内存分配器的定义 内存分配器的定义是用main函数中的第一条语句来完成的: AutoFreeAlloc alloc; 这条语句很简单,但一些幕后的细节我们需要了解。我们再来看AutoFreeAlloc是有一个typedef来定义的: typedef AutoFreeAllocT<StdLibAlloc> AutoFreeAlloc; 而StdLibAlloc的定义如下: struct StdLibAlloc { static void* allocate(size_t cb) { return malloc(cb); } static void* allocate(size_t cb, DestructorType fn) { return malloc(cb); } static void* allocate(size_t cb, int fnZero) { return malloc(cb); } static void deallocate(void* p) { free(p); } static void deallocate(void* p, size_t) { free(p); } static void swap(StdLibAlloc& o) {} }; 从这里可以看出,我们使用AutoFreeAllocT来管理分配的内存,StdLibAlloc进行实际的内存分配(只是简单的使用alloc、free函数对)。这样就提供了一种机制,我们可以自己定义实际的内存分配方式。 现实世界中,充满了各种各样的内存分配方式。比如说我们需要创建映射文件(CreateFileMapping)来作为内存使用,或者说我们要使用共享内存,都可以建立自己的分配器,然后统一用AutoFreeAllocT进行管理(只要建立的分配器符合要求—类似于StdLibAlloc实现6个函数)。 一般情况下,只要直接使用AutoFreeAlloc就可以了。但为了适应特别需要,这里简单介绍了一下StdLibAlloc。 为对象分配内存 为对象分配内存时非常简单,只要使用以下任意一个宏即可: STD_NEW(alloc, Type) STD_ALLOC(alloc, Type) 其中的alloc指定分配器,Type指定对象类型。STD_NEW与STD_ALLOC之间的区别下文说明。 为数组分配内存 为数组分配内存时,可以使用以下宏: STD_NEW_ARRAY(alloc, Type, count) STD_ALLOC_ARRAY(alloc, Type, count) 其中的alloc指定分配器,Type指定对象类型,count指定数组的大小。STD_NEW_ARRAY与STD_ALLOC_ARRAY之间的区别下文说明。 STD_NEW(STD_NEW_ARRAY)与STD_ALLOC(STD_ALLOC_ARRAY)的区别 STD_NEW宏先分配内存,然后使用placement new操作符来构造对象,释放内存时先调用被构造对象的析构函数,然后再释放内存(针对C++中的原生类型(int,char,long,double等),会使用模板特化技术,消除对原生类型的析构调用)。 STD_ALLOC宏只是分配内存,释放内存时直接释放。 STD_NEW_ARRAY与STD_ALLOC_ARRAY之间的区别与此类似。 释放所分配的内存 AutoFreeAlloc会自动释放内存,你也可以通过调用AutoFreeAllo的成员函数clear()手工释放内存(通常不需要)。 一点补充:释放内存时如何调用对象的析构函数 虽然是否知道AutoFreeAlloc释放内存时如何调用对象的析构函数对我们使用AutoFreeAlloc没有影响,但理解这一点,在我们使用AutoFreeAlloc时更能够做到心中有数。 我们知道(参照xushiwei的最袖珍的垃圾回收器),在AutoFreeAlloc的allocate函数中指定了对象的清除函数(这里使用清除是为了与析构区别开来),请见如下代码: template <class _Alloc, int _MemBlockSize = MEMORY_BLOCK_SIZE> class AutoFreeAllocT { void* winx_call allocate(size_t cb, DestructorType fn); }; 其中的DestructorType参数指定了对象的清除函数。其定义如下: typedef void __FnDestructor(void* data); typedef __FnDestructor* DestructorType; 我们再来看STD_NEW的宏定义: #define STD_NEW(alloc, Type) / ::new((alloc).allocate(sizeof(Type), / std::DestructorTraits<Type>::destruct)) Type DestructorTraits的定义如下: template <class Type> struct DestructorTraits { static void winx_call destruct(void* data) { ((Type*)data)->~Type(); } }; 通过这些代码片段,我们就可以知道AutoFreeAlloc释放内存时会调用DestructorTraits<Type>::destruct函数,而在DestructorTraits<Type>::destruct函数中会调用对象的析构函数。 对于数组,其做法与此类似,这里就不详细讲了。
更多
0
|
|