[讨论贴]给C++添加属性机制

pankun 2006-02-09 01:16:15
目标:用C++实现类拟于C#, Delphi的属性机制,即可以在给属性赋值或访问属性时,自动调用设定的get,set函数,且尽量声明,使用方便.
假定要添加属性的类是目标类,给C++添加属性机制,我的想法是建立一个类,重载此类的 "=" 操作符,这样给这个类赋值时,会调用此类的operator = 函数,在此函数中调用目标类的类成员函数即可.但要调用目标类的类成员函数,需要目标类类指针和类成员函数指针.类成员函数指针的类型可以通过模板传入. ,这样属性是类型安全的,并和访问普通成员一样.
缺点是会增加目标类的大小,属性函数必须为public的,另外为了让属性类能正确的取得目标类类指针,我使用了一个INIT_PROPERTY宏,这样比较麻烦,需要在构造函数中对每个属性调用一下,征集更好办法.代码如下(未完善,VC7编译通过)

Property.h

/*
by 剑神一笑
属性定义单元
*/

#ifndef PROPERTY_H
#define PROPERTY_H


//辅助类,用来定义类成员函数指针类型
template<typename T, typename U>
class PropertyType
{
public:
//指向类成员函数的指针类型
typedef void (U::*SetProc)(const T&);
typedef const T& (U::*GetProc)();
};

//属性基类
template<typename U>
class PropertyBase
{
protected:
//拥有这个属性的类的指针
U* obj_;
public:
void SetObj(U *obj)
{
this->obj_ = obj;
}
};

//只写属性过程定义
template<typename T, typename U, typename PropertyType<T, U>::SetProc Set>
class WriteProperty: public PropertyBase<U>
{
protected:
//定义属性set的函数指针
typename PropertyType<T, U>::SetProc SetValue;
public:
WriteProperty()
{
this->SetValue = Set;
}

void operator= (const T &value) const
{
(obj_->*SetValue)(value);
}
};

//只读属性过程定义
template<typename T, typename U, typename PropertyType<T, U>::GetProc Get>
class ReadProperty: public PropertyBase<U>
{
private:
//避免让只读属性可写
void operator= (const T&) {}
void operator= (const ReadProperty<T, U, Get>&) {}
protected:
//定义属性get的函数指针
typename PropertyType<T, U>::GetProc GetValue;
public:
ReadProperty()
{
this->GetValue = Get;
}

operator T() const
{
return (obj_->*GetValue)();
}
};

template<typename T, typename U, typename PropertyType<T, U>::GetProc Get>
std::ostream& operator << (std::ostream &out, const ReadProperty<T, U, Get>& rv)
{
out << rv.operator T();
return out;
}

//读写属性过程定义
template<typename T, typename U, typename PropertyType<T, U>::SetProc Set, typename PropertyType<T, U>::GetProc Get>
class ReadWriteProperty: public PropertyBase<U>
{
private:
typename PropertyType<T, U>::SetProc SetValue;
typename PropertyType<T, U>::GetProc GetValue;

//禁用赋值和拷贝构造
const ReadWriteProperty<T, U, Set, Get>& operator= (const ReadWriteProperty<T, U, Set, Get>&) {}
ReadWriteProperty(ReadWriteProperty<T, U, Set, Get>&) {}
public:
ReadWriteProperty()
{
SetValue = Set;
GetValue = Get;
}

const ReadWriteProperty<T, U, Set, Get>& operator= (const T &value) const
{
(obj_->*SetValue)(value);
return *this;
}

operator T() const
{
return (obj_->*GetValue)();
}
};

template<typename T, typename U, typename PropertyType<T, U>::SetProc Set, typename PropertyType<T, U>::GetProc Get>
std::ostream& operator << (std::ostream &out, const ReadWriteProperty<T, U, Set, Get>& rv)
{
out << rv.operator T();
return out;
}

//简化函性定义的宏
//定义读写属性
#define PROPERTY_DECLARE_RW(property_name, type, class_type, set, get) \
ReadWriteProperty<type, class_type, class_type::set, class_type::get> property_name;
//定义只读属性
#define PROPERTY_DECLARE_R(property_name, type, class_type, get) \
ReadProperty<type, class_type, class_type::get> property_name;
//定义只写属性
#define PROPERTY_DECLARE_W(property_name, type, class_type, set) \
WriteProperty<type, class_type, class_type::set> property_name;

#define INIT_PROPERTY(property_name) property_name.SetObj(this);

#endif//PROPERTY_H

//-------------------------华丽的分隔线-----------------------------

测试代码

#include <iostream>
#include <string>
#include "Property.h"

using std::cin;
using std::cout;
using std::string;

class Test
{
private:
int value_;
string name_;
public:
Test(int value)
{
INIT_PROPERTY(Value);
INIT_PROPERTY(Name);
INIT_PROPERTY(WValue);

this->value_ = value;
name_ = "TestClass";
}

void SetValue(const int& value)
{
cout << "Set Value: " << value << std::endl;
this->value_ = value;
}

const int& GetValue()
{
cout << "Get Value: " << value_ << std::endl;
return value_;
}

const string& GetName()
{
return name_;
}

void ShowValue()
{
cout << "Value: " << value_ << std::endl;
}

//声明可读写属性,参数为: 属性名,属性类型,当前类名,Set函数,Get函数
PROPERTY_DECLARE_RW(Value, int, Test, SetValue, GetValue);
PROPERTY_DECLARE_R(Name, string, Test, GetName);
PROPERTY_DECLARE_W(WValue, int , Test, SetValue);
};


int main()
{
Test t(100);
t.ShowValue();
t.WValue = 999; //只写属性可以写入
//int i = t.WValue; //只读属性无法读取
t.Value = 200; //读写属性可以写入
int i = t.Value; //读写属性可以读取
cout << "i: " << i << std::endl;
cout << t.Name << std::endl; //只读属性可以读取
//t.Name = "hello"; //只写属性无法写入

cin.get();
return 0;
}

运行结果:
Value: 100
Set Value: 999
Set Value: 200
Get Value: 200
i: 200
TestClass
...全文
440 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
xzgyb 2006-02-13
  • 打赏
  • 举报
回复
pankun:哈,是啊
pankun 2006-02-13
  • 打赏
  • 举报
回复
to: xzgyb(老达摩)
上面写错了,应该是Value类的this指针 - (int)(&((Test*)NULL)->Value) 为owner的类指针.
呵呵,看来还是只有我们DELPHI出身的才关心属性啊。
xzgyb 2006-02-13
  • 打赏
  • 举报
回复
pankun:
呵,不客气,
明白了
pankun 2006-02-13
  • 打赏
  • 举报
回复
谢谢老达摩的意见!
昨天和龙子龙孙讨论时,他给出了一种直接从属性类指针算出拥有它的类的指针.
比如有一个属性类Value,他是Test类的公有成员, (int)(&((Test*)NULL)->Value) 能直接得到这个类在Test类中的偏移,因为是非指针成员类,C++只会给Test类分配一次内存,Value类占用的内存也包含在其中.所以 Value类的this指针+(int)(&((Test*)NULL)->Value) 为owner的类指针.一会儿给个例子.

Jagen在路上 2006-02-13
  • 打赏
  • 举报
回复
看来楼主费了不少脑细胞,呵呵:)
xzgyb 2006-02-13
  • 打赏
  • 举报
回复
main.cpp:

#include <iostream>
#include <string>
#include "Property.h"

using std::cin;
using std::cout;
using std::string;

class Test : private Propertyable
{
public:
Test(int value)
{
this->value_ = value;
name_ = "TestClass";
}
void ShowValue()
{
cout << "Value: " << value_ << std::endl;
}



private:
int value_;
string name_;


void SetValue(const int& value)
{
cout << "Set Value: " << value << std::endl;
this->value_ = value;
}

const int& GetValue()
{
cout << "Get Value: " << value_ << std::endl;
return value_;
}

const string& GetName()
{
return name_;
}

public:
//声明可读写属性,参数为: 属性名,属性类型,当前类名,Set函数,Get函数
PROPERTY_DECLARE_RW(Value, int, Test, SetValue, GetValue);
PROPERTY_DECLARE_R(Name, string, Test, GetName);
PROPERTY_DECLARE_W(WValue, int , Test, SetValue);

};


int main()
{
Test t(100);
t.ShowValue();
t.WValue = 999; //只写属性可以写入
//int i = t.WValue; //只读属性无法读取
t.Value = 200; //读写属性可以写入
int i = t.Value; //读写属性可以读取
cout << "i: " << i << std::endl;
cout << t.Name << std::endl; //只读属性可以读取
//t.Name = "hello"; //只写属性无法写入

cin.get();
return 0;
}
xzgyb 2006-02-13
  • 打赏
  • 举报
回复
改了一些,可能有些问题
如下
Property.h:

/*
by 剑神一笑
属性定义单元
*/

#ifndef PROPERTY_H
#define PROPERTY_H

template<typename T, typename U>
class PropertyType
{
public:
//指向类成员函数的指针类型
typedef void (U::*SetProc)(const T&);
typedef const T& (U::*GetProc)();
};



//属性基类
template<typename U>
class PropertyBase
{
protected:
//拥有这个属性的类的指针
U* obj_;
public:
PropertyBase();
};

class Propertyable
{
template <typename U> friend class PropertyBase;
protected:
Propertyable()
{
tmpThis_ = this;
}
private:
static Propertyable * tmpThis_;
};

Propertyable * Propertyable::tmpThis_ = NULL;

template <typename U>
inline
PropertyBase<U>::PropertyBase()
{
obj_ = static_cast< U * >( Propertyable::tmpThis_ );
}

//只写属性过程定义
template<typename T, typename U, typename PropertyType<T, U>::SetProc Set>
class WriteProperty: public PropertyBase<U>
{
protected:
//定义属性set的函数指针
typename PropertyType<T, U>::SetProc SetValue;
public:
WriteProperty()
{
this->SetValue = Set;
}

void operator= (const T &value) const
{
(obj_->*SetValue)(value);
}
};

//只读属性过程定义
template<typename T, typename U, typename PropertyType<T, U>::GetProc Get>
class ReadProperty: public PropertyBase<U>
{
private:
//避免让只读属性可写
void operator= (const T&) {}
void operator= (const ReadProperty<T, U, Get>&) {}
protected:
//定义属性get的函数指针
typename PropertyType<T, U>::GetProc GetValue;
public:
ReadProperty()
{
this->GetValue = Get;
}

operator T() const
{
return (obj_->*GetValue)();
}
};

template<typename T, typename U, typename PropertyType<T, U>::GetProc Get>
std::ostream& operator << (std::ostream &out, const ReadProperty<T, U, Get>& rv)
{
out << rv.operator T();
return out;
}

//读写属性过程定义
template<typename T, typename U, typename PropertyType<T, U>::SetProc Set, typename PropertyType<T, U>::GetProc Get>
class ReadWriteProperty: public PropertyBase<U>
{
private:
typename PropertyType<T, U>::SetProc SetValue;
typename PropertyType<T, U>::GetProc GetValue;

//禁用赋值和拷贝构造
const ReadWriteProperty<T, U, Set, Get>& operator= (const ReadWriteProperty<T, U, Set, Get>&) {}
ReadWriteProperty(ReadWriteProperty<T, U, Set, Get>&) {}
public:
ReadWriteProperty()
{
SetValue = Set;
GetValue = Get;
}

const ReadWriteProperty<T, U, Set, Get>& operator= (const T &value) const
{
(obj_->*SetValue)(value);
return *this;
}

operator T() const
{
return (obj_->*GetValue)();
}
};

template<typename T, typename U, typename PropertyType<T, U>::SetProc Set, typename PropertyType<T, U>::GetProc Get>
std::ostream& operator << (std::ostream &out, const ReadWriteProperty<T, U, Set, Get>& rv)
{
out << rv.operator T();
return out;
}

//简化函性定义的宏
//定义读写属性
#define PROPERTY_DECLARE_RW(property_name, type, class_type, set, get) \
friend class ReadWriteProperty<type, class_type, class_type::set, class_type::get>;\
ReadWriteProperty<type, class_type, class_type::set, class_type::get> property_name;

//定义只读属性
#define PROPERTY_DECLARE_R(property_name, type, class_type, get) \
friend class ReadProperty<type, class_type, class_type::get>;\
ReadProperty<type, class_type, class_type::get> property_name;
//定义只写属性
#define PROPERTY_DECLARE_W(property_name, type, class_type, set) \
friend class WriteProperty<type, class_type, class_type::set>;\
WriteProperty<type, class_type, class_type::set> property_name;

#endif


pankun 2006-02-13
  • 打赏
  • 举报
回复
to: yeyuboy(海绵)
你的办法不是我要实现的属性啊。
你的办法需要每个属性实现一个类了
yeyuboy 2006-02-13
  • 打赏
  • 举报
回复
代码笔误:
include <iostream>改为#include <iostream>
yeyuboy 2006-02-13
  • 打赏
  • 举报
回复
//突然想到的实现,实现得也够简单了,没花时间进行严格的类型推导了,后面的继续完善。
//示例如下:
include <iostream>
using namespace std;

template<typename T>
struct property
{
property(const T& o)
:handle(o)
{}

property<T>& operator=(const T& o) //set
{
cout << "set" << endl;
handle = o;
return *this;
}
operator T() //get
{
cout << "get" << endl;
return handle;
}

T handle;
};


struct Obj
{
property<int> value;
Obj(int v)
:value(v)
{}
};

void main()
{
Obj obj(0);
obj.value = 1; //set
int i = obj.value; //get
}
pankun 2006-02-13
  • 打赏
  • 举报
回复
嗯,代码如下,这样就可以直接在属性类中得到目标类的指针。
谢谢老达摩和龙子龙孙,等等看还有不有其它更简便的办法,然后结贴。

#include <iostream>

using std::cout;
using std::cin;
using std::endl;

template<typename T, typename int(*GetOffset)()>
class Property
{
public:
Property()
{
cout << "this:" << (int)this << std::endl;
cout << "owner:" << (int)this - GetOffset() << std::endl;
}
};

#define PROPERTY_DECLARE(pro_name, type_name) \
private: int static pro_name##_offset() {return (int)(&((type_name*)NULL)->pro_name);};\
public: Property<type_name, type_name::pro_name##_offset> pro_name;

class PropertyTest
{
public:
int i;
public:
PROPERTY_DECLARE(Name, PropertyTest)
};

int main()
{
PropertyTest test;
cout << std::endl;
cout << "property address:" << (int)(&test.Name) << std::endl;
cout << "test class address:" << (int)&test << std::endl;

std::cin.get();
return 0;
}
pankun 2006-02-10
  • 打赏
  • 举报
回复
希望大家给些改进的意见啊.每次要使用前都要INIT_PROPERTY太麻烦了.
ericqxg007 2006-02-10
  • 打赏
  • 举报
回复
难道楼主想做个类似delphi的软件啊?
ancient 2006-02-10
  • 打赏
  • 举报
回复
很不错,收藏
不过这样会实例化很多个模板类
shootingstars 2006-02-10
  • 打赏
  • 举报
回复
好东西,学习。
beyondtkl 2006-02-10
  • 打赏
  • 举报
回复
呵呵 property 是不错的机制...
pagechen 2006-02-10
  • 打赏
  • 举报
回复
you are so pure a OO.
wshcdr 2006-02-10
  • 打赏
  • 举报
回复
建议LZ去看ATL中属性的实现,感觉有点类似


insanehh 2006-02-10
  • 打赏
  • 举报
回复
學習
xzgyb 2006-02-10
  • 打赏
  • 举报
回复
vc本身支持这种扩展,但不是标准,而且get,put函数
好像都必须可见的




class A
{
public:
__declspec( property( get = getName, put = putName ) ) string name;

string getName()
{
return name_;
}

void putName( const string & name )
{
name_ = name;
}

string name_;
};
加载更多回复(6)

64,639

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧