在C++中實現“屬性 (Property)”


摘要:

 
本文介紹了在C++中實現“屬性 (Property)”的方法,“屬性”是我們在C#(或其它一些語言)中常常能用到的一種特性。這里介紹的實現方法使用的是標准的C++,沒有用任何其它的語言擴展。而大部分的庫或是編譯器為了實現“屬性”,往往對C++作一些擴展,就像我們在托管的C++或是C++ Builder中看到的那樣,也有的是使用普通的set和get方法,這些都不能算是真正的“屬性”。
 
 
 
正文:
 
首先,讓我們來看看什么是“屬性”。“屬性”在外觀上看起來就像類中的一個普通成員變量(或者稱為是“字段”),但它內部是通過一組set/get方法(或稱作read/write方法)來訪問類中實際的成員變量。
 
舉例來說,如果我有一個類A和它的一個“屬性”Count,我就可以寫出如下的代碼:
 
A foo;
 
cout << foo.Count;
 
Count實際上是調用了一個get函數,並返回了我們所希望得到的成員變量值。使用“屬性”而不是直接使用成員變量值的最大好處是你可控制這個“屬性”是只讀的(您只能讀出它的值而不能改變它的值)、只寫的、或是可讀可寫的。讓我們一起來實現它吧:
 
我們希望能實現下面的用法:
 
int i = foo.Count; //-- 實際上調用get函數來獲取實際的成員變量的值 --
 
foo.Count = i;   //-- 實際上將調用set函數來設置實際的成員變量的值 --
 
因此,很明顯的,我們需要重載“=”運算符以便可以設置“屬性”的值,還要正確處理“屬性”的返回類型(稍后就可以看到一點)。
 
我們將實現一個類,名叫property,它將表現得像一個“屬性”,它的結構如下:
 
template<typename Container, typename ValueType, int nPropType>
 
class property {}
 
這個類模版將表現為我們需要的“屬性”。Container是一個類的類型(后面我們稱之為“容量類”),這個類就是包含要實現為“屬性”的實際成員變量、訪問這個變量的set/get方法和表現出來的“屬性”的類。ValueType是容量類內部的實際成員變量的類型(也將成為“屬性”的類型),nPropType表示“屬性”的類別:“只讀”、“只寫”或是“讀寫”。
 
我們還需要設置一組指針,指向容器類特定成員變量的set和get方法,同時還要重載“=”運算符,使得“屬性”可以表現得像一個變量。下面我們看看property類的完整程序。
 
#define READ_ONLY 1
 
#define WRITE_ONLY 2
 
#define READ_WRITE 3
 
 
 
template <typename Container, typename ValueType, int nPropType>
 
class property
 
{
 
public:
 
property()
 
{
 
  m_cObject = NULL;
 
  Set = NULL;
 
  Get = NULL;
 
}
 
//-- This to set a pointer to the class that contain the
 
//   property --
 
void setContainer(Container* cObject)
 
{
 
  m_cObject = cObject;
 
}
 
//-- Set the set member function that will change the value --
 
void setter(void (Container::*pSet)(ValueType value))
 
{
 
  if((nPropType == WRITE_ONLY) || (nPropType == READ_WRITE))
 
    Set = pSet;
 
  else
 
    Set = NULL;
 
}
 
//-- Set the get member function that will retrieve the value --
 
void getter(ValueType (Container::*pGet)())
 
{
 
  if((nPropType == READ_ONLY) || (nPropType == READ_WRITE))
 
    Get = pGet;
 
  else
 
    Get = NULL;
 
}
 
//-- Overload the '=' sign to set the value using the set
 
//   member --
 
ValueType operator =(const ValueType& value)
 
{
 
  assert(m_cObject != NULL);
 
  assert(Set != NULL);
 
  (m_cObject->*Set)(value);
 
  return value;
 
}
 
//-- To make possible to cast the property class to the
 
//   internal type --
 
operator ValueType()
 
{
 
  assert(m_cObject != NULL);
 
  assert(Get != NULL);
 
  return (m_cObject->*Get)();
 
}
 
private:
 
  Container* m_cObject;  //-- Pointer to the module that
 
                         //   contains the property --
 
  void (Container::*Set)(ValueType value);
 
                         //-- Pointer to set member function --
 
  ValueType (Container::*Get)();
 
                         //-- Pointer to get member function --
 
};
 
 
 
讓我們來一段段的分析程序:
 
下面這段代碼把Container指針指向一個有效的對象,這個對象就是我們要添加“屬性”的類(也就是容器類)的對象。
 
void setContainer(Container * cObject)
 
 
  m_cObject = cObject;
 
}
 
下面這段代碼,設置指針指向容器類的set/get成員函數。這里僅有的一點限制是set函數必須是帶一個參數且返回void的函數,而get函數必須不帶參數且返回ValueType型的值。
 
//-- Set the set member function that will change the value --
 
void setter(void (Container::*pSet)(ValueType value))
 
{
 
  if((nPropType == WRITE_ONLY) || (nPropType == READ_WRITE))
 
    Set = pSet;
 
  else
 
    Set = NULL;
 
}
 
//-- Set the get member function that will retrieve the value --
 
void getter(ValueType (Container::*pGet)())
 
{
 
  if((nPropType == READ_ONLY) || (nPropType == READ_WRITE))
 
    Get = pGet;
 
  else
 
    Get = NULL;
 
}
 
下面這段代碼,首先是對“=”運算符進行了重載,它調用了容器類的set成員函數以實現賦值操作。然后是定義了一個轉換函數,它返回get函數的返回值,這使整個property類表現得像一個ValueType型的成員變量。
 
//-- Overload the '=' sign to set the value using the set member --
 
ValueType operator =(const ValueType& value)
 
{
 
  assert(m_cObject != NULL);
 
  assert(Set != NULL);
 
  (m_cObject->*Set)(value);
 
  return value;
 
}
 
//-- To make possible to cast the property class to the
 
//   internal type --
 
operator ValueType()
 
{
 
  assert(m_cObject != NULL);
 
  assert(Get != NULL);
 
  return (m_cObject->*Get)();
 
}
 
 
 
下面讓我們看看我們是如何來使用這個property類的:
 
就像下面的代碼中所展示的一樣:PropTest類實現了一個名為Count的“屬性”。這個“屬性”的值實際上是通過get函數從一個名為m_nCount的私有變量獲得並通過set函數把這個“屬性”值的變動寫回到m_nCount中去的。get/set函數可以任意命名,因為它們是通過它們的函數地址傳遞給property類的,就像您在PropTest類的構造函數中看到的那樣。PropTest類中的"property<PropTest,int,READ_WRITE> Count; "一行就使我們的PropTest類就擁有了一個名叫Count可以被讀寫的整型“屬性”了。您可以把Count當成是一個普通的成員變量一樣來使用,而實際上,對Count的讀寫都是通過set/get函數間接的實現的。
 
PropTest類的構造函數中所做的初始化工作是必須的,只有這樣才能保證定義的“屬性”可以正常的工作。
 
class PropTest
 
{
 
public:
 
  PropTest()
 
  {
 
    Count.setContainer(this);
 
    Count.setter(&PropTest::setCount);
 
    Count.getter(&PropTest::getCount);
 
  }
 
  int getCount()
 
  {
 
    return m_nCount;
 
  }
 
  void setCount(int nCount)
 
  {
 
    m_nCount = nCount;
 
  }
 
  property<PropTest,int,READ_WRITE> Count;
 
 
 
 
 
private:
 
  int m_nCount;
 
};
 
 
 
就像下面演示那樣,您可把Count“屬性”當成是一個普通成員變量一樣來使用:
 
int i = 5,j;
 
PropTest test;
 
test.Count = i;    //-- call the set method --
 
j= test.Count;     //-- call the get method --
 
如果您希望您定義的“屬性”是只讀的,您可以這樣做:
 
property<PropTest,int,READ_ONLY > Count;
 
如果希望是只寫的,就這樣做:
 
property<PropTest,int,WRITE_ONLY > Count;
 
注意:如果您把“屬性”設成是只讀的而試圖去改寫它,將會導致一個assertion(斷言)。如果“屬性”是只寫的而您試圖去讀它,也會發生同樣的情況。
 
 
 
總結:
 
本文介紹了如何僅僅使用標准C++的特性在C++類中實現一個“屬性”。當然,直接調用set/get函數會比使用“屬性”效率更高,因為要使用 “屬性”,您就必須為類的每一個“屬性”來實例化一個property類的對象。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM