分享
 
 
 

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

王朝other·作者佚名  2006-11-24
窄屏简体版  字體: |||超大  

//转贴自我的朋友云风的一篇文章,

//里面有些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 必要的. 最后应该优化掉了.

我不知道有什么方法可以写的更漂亮一点

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有