电信主站 网通分站
购买流程 付款方式 常见问题 在线提问 续租服务 购物车
用户名: 密 码: 忘记密码?
首 页
域名注册
虚拟主机
双线主机
服务器租用
VPS主机
企业邮局
代理专区
客服中心
虚拟主机行业资讯 虚拟主机评测对比 互联网最新动态 技术学院 站长资讯 在线教程 网站运营
搜索优化 服务器 网络编程 图形图象 站长之家 网页制作 操作系统
冲浪宝典 软件教学 视频通信 办公软件 邮件系统 网络安全 认证考试
您当前位置:西部数码->资讯中心-> 服务器
模拟虚构造函数的内存分配优化
作者:未知 点击:0
  西部数码-全国虚拟主机10强!20余项虚拟主机管理功能,全国领先!第6代双线路虚拟主机,南北访问畅通无阻!虚拟主机可在线rar解压,自动数据恢复设置虚拟目录等.虚拟主机免费赠送访问统计,企业邮局.Cn域名注册10元/年,自助建站480元起,免费试用7天,满意再付款!P4主机租用799元/月.月付免压金!
文章页数:[1] 
//转贴自我的朋友云风的一篇文章,
//里面有些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;

// 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); }
};

// 现在我们可以方便的让 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 的自动选择

代码:--------------------------------------------------------------------------------
template < class T, int type_id > struct __var_class {
typedef _var_direct_value _direct;
typedef _var_pointer _pointer;
template < int _Small > struct _selector {
typedef _pointer _result;
};
template<> struct _selector<1> {
typedef _direct _result;
};
template<> struct _selector<0> {
typedef _pointer _result;
};
typedef _selector< (sizeof(T)+sizeof(var_null)<=sizeof(var::var_data)) > ::_result _best;
};
// 这个template的写法来自 STL 头文件,最后一句的意思是用计算sizeof的值来判断选择哪一种 result
--------------------------------------------------------------------------------

ok. 现在使用
__var_class::_best var_string;
来定义一个 var_string 可以放置 stl 的 string

__var_class 这个 template 利用里面的 _selector 来判断 string 的 size
能不能放在 var 预留的空间里面, 把使用上面那个 template 最合适的结果类型
赋给 _best. 我们将 __var_class::_best 定义成喜欢的短名字就 ok 了

或许上面的写法还太烦琐, 定义个宏好了 :)

#define DECLARE_VAR(type_name,id) typedef __var_class::_best var_##type_name;

下面定义个 var_double 玩玩

DECLARE_VAR(double,'doub')

var_double 就可以存放 double 了.

一般我们还希望用 var s=var_string("hello");
后面可以用
string ss=var.as_string(); 这样取出来.
为每种新类型加一个 as_xxx() 函数是不实际的, 可以用一个成员函数模板实现.
先在每个类型里加一个 traits

在 _var_direct_value 和 _var_pointer 模板里加上
typedef T& reference; (萃取类型用)
分别加上
T& get_value() { return data; }
T& get_value() { return *data; }
获得引用.

然后在 var 里加一个 template function
template T::reference as(T t) {
assert(is(T::type));
return ((T*)&(data.obj))->get_value();
}

大功告成

var s=var_string("hello");
string ss=var.as(var_string()); 这样可以取出前面 var 里放的 "hello" 来.
var_string() 这个参数是编译器实例化 var:as 必要的. 最后应该优化掉了.
我不知道有什么方法可以写的更漂亮一点
文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!
相关主题
文章页数:[1] 
Google
热门文章
·LINUX学习笔记--FTP服务器设置篇
·在内核2.6.0-test中安装最新驱动NVIDIA 1.0-4620(共三种方法)
·无软驱和光驱安装Redhat方法
·如何通过PXE的方式远程安装linux(本地无 光 , 软驱)
·网络安装的一点实践
·VI 快速上手
·Linux下Apache并发连接数和带宽控制
·轻轻松松的安装Slackware Linux -- 5.其他非标准安装程序解析
·在Linux下建立强大的FTP搜索引擎(二
·Iptables来限制上QQ

最新文章
·造成服务器不稳定的因素有以下七点
·机房是海外服务器安全稳定重要因素
·UniCache 行业垂直门户网站加速方案
·VPS服务器是主机业务的革命性技术
·如何选好虚拟主机的五个关键细节
·虚拟化技术会造成服务器市场的低迷吗?
·IP KVM打造“保姆”服务的IDC机房
·服务器能耗吞噬资源 优化数据中心
·ASP.NET2.0服务器控件之捕获回传事件
·服务器虚拟化必须考虑的十大重要因素


 
 


版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!

特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。
  打印  刷新  关闭
返回首页 |关于我们 | 联系我们 | 付款方式 | 创业联盟 | 虚拟主机 | 资讯中心 | 友情链接 | 网站地图

版权所有 西部数码(www.west263.com)
CopyRight (c) 2002~2006 west263.com all right reserved.
公司地址:四川成都市万和路90号天象大厦4楼 邮编:610031
电话总机:028-86262244 86263048 86263408 86263960 86264018 86267838
售前咨询:总机转201 202 203 204 206 208
售后服务:总机转211 212 213 214
财务咨询:总机转224 223 传真:028-86264041 财务QQ:点击发送消息给对方635483282
售前咨询QQ:点击发送消息给对方2182518 点击发送消息给对方241975952 点击发送消息给对方275026793 点击发送消息给对方408235859
售后服务QQ:点击发送消息给对方17708515 点击发送消息给对方307742704 点击发送消息给对方287976517 点击发送消息给对方363783715
《中华人民共和国增值电信业务经营许可证》编号:川B2-20030065号