|
|

楼主 |
发表于 2008-2-23 03:36:00
|
显示全部楼层
[LoserPower]运行时、资源、句柄、对象层、属性
1、运行时:
为类交叉引用和其他全局服务创建运行时库。除未知符号外,一般编译依然依据库文件尽可能在线编译。
2、资源:
1)光标
class Cursor
:public Handle<HCURSOR>
Type(const tstring& res, HINSTANCE hinst = 0)
:Base(LoadIcon(hinst, res.c_str()), true)//Shared resource
{}
构造时向基类传递,创建共享句柄(析构时不释放资源)
2)图标
class Icon
:public Handle<HICON>
3、句柄:
template<typename HdrTy> class Handle
: public Object
1)包含一个状态开关用于向释放回路表明资源当前已经释放:
void Released()
{
_HoldHandle = false;
}
2)手动启用资源的共享模式,如窗口类的资源指定和替换动作需要维护该共享模式,以便多个窗口共享一套资源:
bool SharedHandle(bool shared_ = true)
{
bool oState = _FromHandle;
_FromHandle = shared_;
return oState;
}
3)复合体用于代码自解释和访问便利性:
union
{
HdrTy handle; //基础句柄
struct
{
HdrTy _HoldHandle; //bool值,是否持有句柄
};
//句柄别名
struct
{
HWND hwnd; //窗口对象的句柄别名
};
struct
{
HDC hdc; //设备表对象的句柄别名
};
};
应用,进行类画刷替换,资源替换前应取消共享,否则旧有资源将因得不到释放而泄漏:
Brush SetClassBrush(const Brush& br_= Color::clrBtnFace)
{
Brush oBr = _ClsBrush;
//取消共享
_ClsBrush.SharedHandle(false);
//替换资源
_ClsBrush = br_;
//设置共享
_ClsBrush.SharedHandle();
if (hwnd)
oBr = HBRUSH(SetClassLong(hwnd, GCL_HBRBACKGROUND, long(_ClsBrush.Get())));
return oBr;
}
4、对象层Object Layer。
1)并不是严格的运行时类型识别。它所携带的信息仅仅是一个层次标识:
class Object
{
BaseClass(Object);
ObjectLayer(_Object);
enum _LayerMask
{
_Object = 1000,
_Handle = 1001,
_DC = 1002,
_PaintDC = 1003,
_GDIObject = 1004,
_Brush = 1005,
_Pen = 1006,
_Window = 1007,
_Icon = 1008,
_Cursor = 1009,
};
};
ObjectLayer(ly)展开:
virtual int LayerId()
{
return ly;
}
2)用例:DC和PaintDC的句柄释放动作:
一般派生类持有的基类资源可由基类管理释放或者宣告结束动作,如GDI包装对象之间的关系,或者窗口控件的关系,固定由一个层次完成相应句柄的回收。但是DC和PaintDC分别用ReleaseDC和EndPaint完成句柄归还。为此需要启用对象层标识:
bool DC::_Release()
{
if (!BaseRelease())
return false;
if (LayerId() == _DC)
{
ReleaseDC(hwnd, hdc);
Released();
}
RestoreBrush();
RestorePen();
RestoreFont();
return true;
}
bool PaintDC::_Release()
{
if (!BaseRelease())
return false;
if (LayerId() == _PaintDC)
EndPaint(hwnd, &ps);
Released();
return true;
}
如上,句柄归还动作需要访问对象层标识,以完成资源释放,并标记为已释放的,这将导致句柄值被清除为0。
当然,完成对象层识别仅需要以下说明段:
ObjectLayer(_DC);
和
ObjectLayer(_PaintDC);
主要标识被登记在Object::_LayerMask,用户自定义派生树需要自己维护其对象层标识。当然通常情况下并不推荐使用。
5、属性:
注意:该实现版本并不是最有效的实现,仅满足于含有
BaseClass(cls);
InheritClass(cls);
指示Base和Type分别为基类和派生类型别的类定义上作为内置的属性。
1)制作属性模板:
template<typename Ty, typename Owner> class PropertyTmpl
{
public:
PropertyTmpl& operator()(Owner* this_, const_reference val = value_type());
reference operator&()
{
return (*this);
}
operator reference()
{
return Get();
}
virtual reference Get() = 0;
virtual value_type operator=(const_reference val) = 0;
//operators
bool operator==(const_reference val_);
bool operator!=(const_reference val_);
bool operator!();
protected:
Owner* owner;
value_type value;
};
该模板向实现类提供了Get和Set接口。因为operator=是不可继承的,总是会被派生类覆盖,因为派生类在不显式定义operator=的时候总是从复制构造中获得执行动作,而实现新版本operator=将一样使得旧版本被覆盖,而这失去了属性的语法纯粹性,派生类不应在机制上依赖宏或者用户实现。
可以将派生类名传回给基类,基类将其定义别名:
typedef ThisTy Set。
然后在派生类按以下格式:
Set(const type& val);
这看似可行,但是有以下问题:第一,不能有返回值;第二,不能使用在具有
宿主资源访问的属性上,而这时绝有可能的情况,因为传入类型将会利用该函数构造出一个有效对象,并利用默认赋值将新对象数据覆盖到属性对象上,而这时候便丢失了宿主指针,发生内存访问错误:
PropertyA = val_a;
除非这样调用:
PropertyA = TypePropertyA(val_a, this);
但是没有人会使用这样的“属性”语法。
2)使用宏修整外观:
#define Property(name, type)
struct _##name :public PropertyTmpl<type, Type>
因为某种原因,宏展开的未命名类型并没有得到编译器的临时串关联,总是生成同样的名称:
__unnamed
而非:
__unnamed_f8d2bd48_1等。
为了该不幸的理由,属性头要指出属性名称以便制作唯一的属性类型名。
#define Set(val)
operator=(val)
同样不幸的,构造式和operator=均为Set的实现带来了麻烦,为此,采用了最简单的办法,将Set展开成operator=。一个好消息是:尽管Get()函数名经常被使用,但是Set()是相当少见的,实在不放心的话为其添加前缀下划线。
3)好了,具备属性了:
property:
Property(ClassName, tstring)
{
tstring& Get()
{
return value;
}
tstring Set(const tstring& val)
{
tstring oName = value;
if (!owner->_HoldHandle || owner->_FromHandle)
value = val;
return oName;
}
}ClassName;
说明如下:一、Get()总是返回数据类型引用。二、不应为Set()指定默认值,这同样是operator=所不支持的,Set()返回值类型。三、value是属性值的界面,owner是宿主指针的界面。四、完成一个属性类型应该同时为其指定属性实例,属性类型名和实例命名不需要配对,为了简单,尽量完成配对。
4)又是麻烦的情况:属性对象构造在声明时不能指定构造数据,在宿主构造式不能访问宿主this指针,仅应在构造体上操作:
Text(this, text_);
ClassName(this, clsname_);
每个构造式均应有针对性地完成该模拟构造过程,它使用了operator(),其中初始化值是可省略的:
Text(this);
ClassName(this);
当然可以不指定宿主,仅仅因为它“麻烦”,要在宿主完成构造的时候才允许访问,但是属性将因此而不能更新宿主状态,仅仅是孤立的数据管理单元。而在界面编程中,这种属性简直一无是处。到处都是关联的且实时变化的属性和宿主状态。
5)属性值的方法调用。
属性实质上是一个值的包装类,并不是值本身,这意味着它要表现出值的特性,要么制作感兴趣的代理接口,如operator==,operator!=,operator!等操作符。要么利用显式方法来传出值引用类型,这就不像外部访问中,仅仅让编译器完成隐式转换那么轻松了,使用ref()和operator&来获取值引用:
reference operator&()
{
return (*this);
}
因为优先级的关系,所以使用operator&需要加括号:
wce.lpszClassName = (&ClassName).c_str();
就像一般内存对象的解地址:
(&Abc)->mtd();
6)所有这一切为了界面:
Text = los::app->LoadString(IDS_MAINWND);
MessageBox(Text);
替代了:
SetText(los::app->LoadString(IDS_MAINWND));
MessageBox(GetText()); |
|