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

       在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 去转换).


   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值