在C++中利用模板實現 data variant(propery)

來源:互聯網
上載者:User

       在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 去轉換).


   

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.