在C++中利用模板实现 data variant(propery)

发布时间:2009年08月31日      浏览次数:1899 次
在C++Builder, 可以通过CB的扩展语法__property()来实现对象的Property。但这个方法必需有编译器的支持,脱离了CB的编译环境就无法使用。同样的,在VC6.0以上的版本中,也实现了__declspec(property)这样一个语法来实现property.它除了跟CB 中的perperty一样,在跨平台方面有局限性以外,而且他不支持多态。 下面的例子可以说明:
  class M
   {
  private:
   int v;
   virtual int Get()
   {
   return v;
   }
   virtual void Set(int x)
   {
   v = x;
   }
  public:
   __declspec(property(get=Get,put=Set)) int XValue;
   };
   class MM : public M
   {
  virtual int Get()
   {
   return v+3;
  }
  virtual void Set(int x)
  {
   v = x -3;
  }
   };
   MM m;
   m.XValue = 3; //实际上调用的是M::Set(),而不是MM::Set()
   int v = m.XValue; //实际上调用的是M::Get(), 而不是MM:Get();
   为什么呢?因为在编译器进行编译的时候,已经将这个property跟 M::Get(),M::Set()邦定在了一起。
   鉴于上面所述,我们提出我们目标:
   1. 要实现Property,并且不依赖于某一个开发平台,只要是当前流行的C++编译器都可以编译通过。
   2. 必须使这种Property的实现方法符合OOP的法则。
   只有达到以上两点,才能使我们的方法有实用性。
   下面我们利用C++的模板方法来实现Property. 这个方法未必是最好的,而且在实际使用的时候还会是有问题的。但也算是学习一下C++的Template了。
   首先来看看Property的基本实现原理。在C++语法中,是没有Property这样的语法的。在C++中,Property就相当于类成员变量,我们可以把成员变量暴露在Public域,这样我们可以任意操作他,而不用很麻烦的去调用某一个操作函数(看上去就象在VB中操作对象的Property一样)。但实际上这是一种很不安全的方法,因为我们失去了对这个成员变量的控制,那将会发生很多意料不到的事情。 因此在C++中,Property的实现都是通过成员函数来模拟实现的。比如:
   class A
   {
   private:
   int x;
   public:
   void get_X(int& v);
   void put_X(int v);
   }
   类A中的成员函数X,就是通过get_X()和put_X()这两个函数来实现访问的。这将保证变量x尽量的处于我们的保护之中(可以在get_X,put_X中写入我们对x的保护代码)。
   那么能否通过某一种方法,来隐藏对get_和put_函数的调用,而类似于:
   A.X_Value = 3; //其实是间接调用A.put_X(3)
   int a = A.X_Value; //其实是间接调用A.get_X();
  这样的语法呢?
   可以通过C++的模板技术来实现。要实现这样的语法,我们需要一个代理模板类, 在这个模板类中,我们重载操作符"=", 在这个重载的operator =()函数中,我们调用宿主对象的put_X()函数,把值传入. 然后我们还要重载属性值类型的操作符, 比如属性值的类型是 string ,则我们要重载一个叫string的操作符, 并在 operator string()中调用宿主对象的get_X()函数,把值传出去.
   下面是例子代码
  #include <stdio.h>
  #include <string>
  using namespace std;
  template<class ClassName , typename ParamType>
  class Property
  {
  public:
   typedef void (ClassName::*Get)(ParamType& ) ; //定义一个指向实际类中进行数据操作的函数指针类型 Get,和 Set
   typedef void (ClassName::*Set)(ParamType& ) ;
   Property()
   {
   };
   Property( Get pGet , Set pSet ,ClassName* pHost ):m_pfnGet(pGet),m_pfnSet(pSet),m_pHost(pHost)
   {
   };
   operator ParamType()
   {
   ParamType v;
   (m_pHost->*m_pfnGet)(v);
   return v;
   };
   ParamType operator = (ParamType v)
   {
   (m_pHost->*m_pfnSet)(v);
   return v;
   };
  private:
   Get m_pfnGet; //保存设置函数的指针
   Set m_pfnSet; //保存获取函数的指针
   ClassName* m_pHost; //保存宿主对象到指针
  };
  class CTestClass
  {
  protected:
   int v;
   virtual void SetValue(int& x )
   {
   v = x;
   };
   virtual void GetValue(int& x )
   {
   x = v;
   };
  public:
   Property<CTestClass,int> Value;
   CTestClass():Value((Property<CTestClass,int>::Get)&CTestClass::GetValue,
   (Property<CTestClass,int>::Set)&CTestClass::SetValue,
   this
   )
   {
   }
  
  };
  //属性值为C++对象的例子
  class CTestClass2
  {
   private:
   string m_str;
   void SetValue(string& s)
   {
   m_str = s;
   };
   void GetValue(string& s)
   {
   s = m_str;
   }
   public:
   Property<CTestClass2,string> Value;
   CTestClass2():Value((Property<CTestClass2,string>::Get)&CTestClass2::GetValue,
   (Property<CTestClass2,string>::Set)&CTestClass2::SetValue,
   this
   )
   {
   }
  };
  //测试子类的函数能否正确调用
  class CChild : public CTestClass
  {
   protected:
   void SetValue(int& x)
   {
   v = x + 5;
   }
  };
  int main(int argc, char* argv[])
  {
   CTestClass m;
   m.Value = 5;
   printf("m.value is %d\n",(int)m.Value);
   CChild n;
   n.Value = 5;
   printf("n.value is %d\n",(int)n.Value);
   CTestClass2 o;
   o.Value = "hello,propery!";
   printf("o.value is %s\n",((string)o.Value).c_str());
   getchar();
   return 0;
  }
   使用G++进行编译.
   g++ property.cpp -o property
   运行 ./property
   输出:
   m.value is 5
   n.value is 10
   o.value is hello,propery!
   因为我们传达给代理类的宿主对象的实际运行指针(this), 所以即使是宿主对象的set,get函数是虚拟函数,我们也能获取到函数的地址,进行调用. 但是这个方法在获取属性值时还是有些缺陷的, 就是必须显式的声明调用类型. 比如上例中, printf("m.value is %d\n",(int)m.Value); 必须在m.Value 前加(int), 否则编译器就不知道m.Value是什么类型,就会报错(也许有的编译器能够自动用Property类的opreator int 去转换).
免责声明:本站相关技术文章信息部分来自网络,目的主要是传播更多信息,如果您认为本站的某些信息侵犯了您的版权,请与我们联系,我们会即时妥善的处理,谢谢合作!