用C++实现属性

Delphi和C#的类都提供了“属性”的概念,使得Getter和Setter的方法可以像访问成员一样简单,如下面的Delphi代码:
TMyClass = class
private
FValue: Integer;
funcation GetValue: Integer;
procedure SetValue(value: Integer);
public
property Value: Integer read GetValue write SetValue;
end;
红色部分就是属性的声明,以后可以直接对Value进行读写,像下面这样:
MyClass.Value := 100;
v = MyClass.Value;
属性有几个显而易见的好处:
  • 与Getter和Setter相比更加简单,就像直接访问成员一样。
  • 与直接访问成员相比,属性可以控制读写权限,并通过Getter和Setter对代码进行检验。
  • 属性对UI的所见即所得编辑很有用,可以很直观的设置某个属性,如大小,颜色等;QT似乎也大量应用属性,不过我没有去看过。
C++原生没有支持属性,但这个语言的特点就是语法强大,通过一些高级属性就可以实现其他语言的特性。要实现属性,C++当然是可以胜任了,而且实现方式有很多,我在网上就看过几种实现。不过在这里我要讲另外两种实现,一种是我想到的,另一种是编译器的扩展。
先看第一种,网上通常的实现是用“模板+操作符重载”的方式,这种方式需要在类的构造函数中初始化模板类成员,并且模板类包含了3个成员:外部类指针和读写函数指针。这个模板类成员就是“属性”,一个属性将浪费掉一些存储,如果一个类存在大量属性,则这个类的空间尺寸是可观的。
我的实现是用“本地类+操作符重载”的方式,用了3种本地类,分别实现“只读,只写,可读写”三种属性,用宏包装起来方便使用,请看下面的代码:
// 取外部类实例指针(this)
// outClass 外部类名
// localClassMem 本地类成员

#define OUTCLASS_THIS(outClass, localClassMem) ((outClass*)((unsigned char*)this - offsetof(outClass, localClassMem)))
// 属性定义宏
// cls 定义属性的类
// type 属性的类型
// propname 属性名
// getter 读函数
// setter 写函数

#define PROPERTY_R(cls, type, propname, getter) \
class property_##getter \
{ \
public: \
operator type () \
{ \
return OUTCLASS_THIS(cls, propname)->getter(); \
} \
} propname
#define PROPERTY_W(cls, type, propname, setter) \
class property_##setter \
{ \
public: \
property_##setter &operator = (const type& value) \
{ \
OUTCLASS_THIS(cls, propname)->setter(value); \
return *this; \
} \
} propname
#define PROPERTY_RW(cls, type, propname, getter, setter) \
class property_##getter_##setter \
{ \
public: \
operator type () \
{ \
return OUTCLASS_THIS(cls, propname)->getter(); \
} \
property_##getter_##setter &operator = (const type& value) \
{ \
OUTCLASS_THIS(cls, propname)->setter(value); \
return *this; \
} \
} propname
有了这几个宏,就可以写一个测试类来看看结果了:
class Test
{
public:
PROPERTY_R(Test, int, ValueR, GetValueR);
PROPERTY_W(Test, float, ValueW, SetValueW);
PROPERTY_RW(Test, bool, ValueRW, GetValueRW, SetValueRW);

Test(): mValueR(100), mValueW(0), mValueRW(false)
{
}
int GetValueR()
{
return mValueR;
}
void SetValueW(const float value)
{
mValueW = value;
}
bool GetValueRW()
{
return mValueRW;
}
void SetValueRW(bool value)
{
mValueRW = value;
}
private:
int mValueR;
float mValueW;
bool mValueRW;
};

int _tmain(int argc, _TCHAR* argv[])
{
Test test;
int v = test.ValueR;
cout<<v<<endl;
test.ValueW = 1.0f;
test.ValueRW = true;
cout<<test.ValueRW<<endl;

return 0;
}
基本上可以满足要求了,内部类没有成员,一个类只占一个字节,比之前面的要节省得多。
尽管用标准语法可以实现“属性”,但仍有一些不足之处,首先是属性若传入有可变参数的函数时会有问题,如下面:
printf("%d", test.ValueR);
编译器识别不出可变参数的具体类型,因此ValueR并没有默认转换为int,要这样写才正确:
printf("%d", (int)test.ValueR);
另外一个问题是“数组属性”如何实现,至少我没有找到更好的办法,若哪位朋友有好的办法,不访分享出来。
如果你确定你的代码只会运行在Windows上,并且只用VC作为编译器,那么就可以用第二种实现。微软提供了property关键字用来支持属性机制,编译器会自动将属性替换为Get或Set函数,这样一来属性就根本不占用任何空间,也没有任何调用开销。
下面同样包装了几个宏,不仅实现了普通属性,也实现了数组类型的属性:
#define PROPERTY_R(type, propname, getter) __declspec(property(get=getter)) type propname
#define PROPERTY_W(type, propname, setter) __declspec(property(put=setter)) type propname
#define PROPERTY_RW(type, propname, getter, setter) __declspec(property(get=getter, put=setter)) type propname
#define PROPERTY_ARRAY_R(type, propname, getter) __declspec(property(get=getter)) type propname[]
#define PROPERTY_ARRAY_W(type, propname, getter) __declspec(property(put=setter)) type propname[]
#define PROPERTY_ARRAY_RW(type, propname, getter, setter) __declspec(property(get=getter, put=setter)) type propname[]
有了编译器的支持,事情的确简单得多了。
C++实现属性 本文译自http://www.codeguru.com/cpp_mfc/Property.html的Implementing a Property in C++ 以下是译文 本文由Emad Barsoum投稿。 开发测试环境:Visual C++ 7.0, Windows XP sp1, Windows 2000 sp3 摘要 本文试着在C++中不使用任何扩展技术模拟C#(或其他语言)中的属性特征。大多数在C++实现属性的库和编译器使用扩展技术,如Managed C++C++ Builder,或者他们使用如通常函数的set和get方法,但那不是属性。 详述 我们首先看一下什么是属性。一个属性表现为一个字段或者成员变量,但它通过read和write方法或者get和set方法暗中操作变量。 例如,若存在类A和它的属性Count,我可以写如下的代码: A foo; Cout << foo.Count; 实际上Count调用它的get函数返回当前的变量值。你可以将属性定为只读(你可以读取它但不能修改它)、只写或者可读写,这就是使用属性而不直接使用变量的的一个最大好处了。好了,让我们开始来实现它: 我们需要能做如下的事: int i = foo.Count; //--调用get函数得到值 foo.Count = i; //-- 调用set函数设定值 因此,很明显的我们需要重载 = 操作符使其能设定变量的值,同时也要重载该属性的返回值(在下面我们将会看到的)。 我们将实现一个称为property的类,它做的就像一个属性,声明如下: template class property {} 这个模板类表示的是我们的属性。Container是我们要在其中包含属性的类变量,set和get方法以及属性的类的类型。ValueType是内部变量即要定义的属性的类型,nPropType定义属性的读写标志:只读、只写或可读写。 现在我们需要一个指向从包含属性的类Container到属性类property的set和get方法的指针,同时重载 = 操作符以使得属性能象变量起那样作用。现在我们来看property类的全部定义 #define READ_ONLY 1 #define WRITE_ONLY 2 #define READ_WRITE 3 template class property { public: property() { m_cObject = NULL; Set = NULL; Get = NULL; } //-- 将m_cObject指向包含属性的container类 -- void setContainer(Container* cObject) { m_cObject = cObject; } //-- 设定可改变属性值的set成员函数 -- void setter(void (Container::*pSet)(ValueType value)) { if((nPropType == WRITE_ONLY) || (nPropType == READ_WRITE)) Set = pSet; else Set = NULL; } //-- 设定可检索属性值的get成员函数 -- void getter(ValueType (Container::*pGet)()) { if((nPropType == READ_ONLY) || (nPropType == READ_WRITE)) Get = pGet; else Get = NULL; } //-- 重载 = 号操作符使其能用set成员设定属性值-- ValueType operator =(const ValueType& value) { assert(m_cObject != NULL); assert(Set != NULL); (m_cObject->*Set)(value); return value; } //-- 使属性类能转换为内部类型成为可能-- operator ValueType() { assert(m_cObject != NULL); assert(Get != NULL);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值