当前位置:Linux教程 - Linux综合 - 模拟虚构造函数的内存分配优化

模拟虚构造函数的内存分配优化

  //转贴自我的朋友云风的一篇文章, //里面有些DarkSpy自己写的注释,希望能给不太懂这篇文章意思的朋友一些提示。 构造函数不能是虚的, 这让人很郁闷. 在 Thinking in C++ 第2版的最后作者给出了一种模拟虚构造 函数的方法, 基本是这样的. 代码:-------------------------------------------------------------------------------- // 给出一个抽象类 shape, 里面有要提供的接口 class shape { public: shape(); virtual ~shape(); virtual void draw(); //.... }; // 别的类用这个派生 class circle : public shape{ public: circle(); ~circle(); void draw(); //... }; class rectangle : public shape { public: rectangle(); ~rectangle(); void draw(); //... }; // 再给一个 shapewrap 封装一下 class shapewarp { protected: shape *object; public: shapewrap(const string &type) { if (type=="circle") object=new circle; else if (type=="rectangle") object=new rectangle; else { // ... } } ~shapewrap() { delete object; } void draw() { object->draw(); } }; -------------------------------------------------------------------------------- 我昨天在做脚本的参数分析的时候, 想给出一个类似 vb 或者 Java 里那样的 var 类型, 能够装下所有不同种类的变量. 基本上的要求更上面的例子很像. 但是出于效率的角度, 考虑到 wrap 类仅仅只有 4 字节, 放了一个对象指针. 无论在何地构造出 wrap 对象, 都会有一个动态的 new 操作 做内存分配, 如果参数表用 stl 的容器装起来, 这些 new 操作 做的内存分配也无法用到 stl 容器的比较高效的内存管理策略. 这让人心里很不舒服, 所以就着手优化这一部分的代码. 开始的核心思想是能够对小尺寸对象不做 2 次内存分配. 解决方案是在 warp 对象里预留一小块空间安置小对象用. 基类和 warp 类就是这样设计的. 代码:-------------------------------------------------------------------------------- class var; // 基类是一个为空的东西 class var_null { public: typedef int var_type; enum { type='null' }; // 类型识别用, 每种类型用一个整数表示 var_null() {} virtual ~var_null() {} void *operator new ( size_t size , var *p); void operator delete (void *p, var *v) {} void *operator new ( size_t size) { return ::operator new(size); } void operator delete (void *p) { ::operator delete(p); } protected: virtual void clone(var *p) const { new(p)var_null; } void copy_to(var *p) const; bool is_type(var_type type) const { return get_type()==type; } virtual var_type get_type() const { return type; } private: virtual void do_copy_to(var_null &des) const {} friend class var; }; // 给出一个 null 是空对象 extern var_null null;
[1] [2] [3] 下一页 

// warp 类 class var { public: var() {} ~var() {} var(const var &init) { init.clone(this); } var(const var_null &init) { init.clone(this); } const var& operator=(const var &src) { src.copy_to(this); return *this; } const var& operator=(const var_null &src) { src.copy_to(this); return *this; } bool is(var_null::var_type type) const { return data.obj.is_type(type); } bool is_null() const { return data.obj.is_type(var_null::type); } var_null::var_type get_type() const { return data.obj.get_type(); } protected: void clone(var *p) const { data.obj.clone(p); } void copy_to(var *p) const { data.obj.copy_to(p); } public: strUCt var_data { var_null obj; int uninitialized[3]; //存放小对象的空间 }; private: var_data data; friend class var_null; }; inline void var_null::copy_to(var *p) const { if (!p->is(get_type())) { p->data.obj.~var_null(); clone(p); } else do_copy_to(p->data.obj); } inline void * var_null::operator new ( size_t size , var *p) { assert(size<=sizeof(var::var_data)); return &(p->data.obj); } -------------------------------------------------------------------------------- 注意 var (warp) 类里面没有放 var_null 的指针, 而是放了一个 var_null 对象的实例. 而且在后面留了一小段空间. 这是这个优化方案的核心. var 在构造的时候同时构造了一个 var_null, 但是, 当我们再赋值的时候, 如果想赋的是一个 var_null 的派生类对象, var_null 的 copy_to 会检查出来, 并且把原来这个地方的对象 析构掉(主动调用析构函数) 但是由于空间是 var 构造的时候就给出的, 所以不需要 释放内存, 然后用 clone 在原地生成一个新的对象. 这里在原地构造新对象是用重载 一个特殊版本的 new 实现的, 看 var_null 的 operator new , 它接受一个 var 指针, 然后计算出原来放 var_null 的位置, 直接返回. 这样, 原来放 var_null 对象的位置, 就放了一个新的 var_null 派生物. 由于 var_nul 的析构函数是虚的, 这个新对象的 析构函数指针位置和原来的相同, 所以 var 在析构的时候, 无论这个位置放的什么 都会正常的析构掉. 现在,由 var 管理的小对象就不需要 2 次内存分配了. 但是 var 里预留的空间有限, 对于大对象, 我们依然需要保存对象指针. 为小对象, 和大对象, 我做了两个不同的 template. 代码:-------------------------------------------------------------------------------- // 直接放值的: template class _var_direct_value : public var_null { public: enum { type=type_id }; _var_direct_value() {} _var_direct_value(T d) : data(d) {} operator T() { return data; } protected: T data; private: var_type get_type() const { return type; } void do_copy_to(var_null &p) const { ((_var_direct_value &)p).data=data; } void clone(var *p) const { new(p) _var_direct_value(data); }
上一页 [1] [2] [3] 下一页 

}; // 现在我们可以方便的让 var_int 可以存放一个 int typedef _var_direct_value var_int; // 放对象指针的: template class _var_pointer : public var_null { public: enum { type=type_id }; _var_pointer() : data(new T) {} _var_pointer(const T &init) : data(new T(init)) {} _var_pointer(const _var_pointer &init) : data(new T(init.data)) {} _var_pointer(const var &init) { init.clone(this); } ~_var_pointer() { delete data; } operator T() { return *data; } const _var_pointer& operator=(const _var_pointer &v) { if (&v!=this) { delete data; data=new T(v.data); } return *this; } protected: T *data; private: var_type get_type() const { return type_id; } void do_copy_to(var_null &p) const { _var_pointer &v=(_var_pointer &)p; *(((_var_pointer &)p).data)=*data; } void clone(var *p) const { new(p) _var_pointer(*data); } }; -------------------------------------------------------------------------------- 看到这里已经累了吗? 可是还没有完 (虽然看起来问题都解决了) 我们可以实现的更完美一些 :) 如果让用户来决定什么时候该使用那个 template 实在是难为他们, 因为 需要计算 var 里的那个空间到底放不放的下想放的东西. 如果更改 var 里预留空间大小, 还会涉及到代码的改变. 所以我使用了一个 template 的技巧来完成template 的自动选择 代码:-------------------------------------------------------------------------------- te

(出处:http://www.sheup.com)


上一页 [1] [2] [3] 

所以我使用了一个 template 的技巧来完成template 的自动选择 代码:-------------------------------------------------------------------------------- te

(出处:http://www.sheup.com)


上一页 [1] [2] [3] [4]