給C++添加屬性機制

    以前用DELPHI和C#時,對DELPHI和C#語法中的屬性感到十分方便,在讀寫屬性時能自動調用屬性的get, set函數或代碼.但我最喜歡的C++中沒有屬性機制.不過C++提供了範型編程和操作符重載機制,足以讓一切變成可能.
    假定要添加屬性的類是目標類,給C++添加屬性機制,我的想法是建立一個類,重載此類的 "=" 操作符,這樣給這個類賦值時,會調用此類的operator = 函數,在此函數中調用目標類的類成員函數即可.但要調用目標類的類成員函數,需要目標類類指針和類成員函數指針.類成員函數指針的類型可以通過模板傳入.廢話不多說,代碼如下:(VC7中編譯通過,GCC還未測試)


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

這種方法是類型安全的,但會讓目標類佔用更多內存,而且屬性調用的函數必須爲public的,另外爲了讓屬性類能正確的取得目標類類指針,我使用了一個INIT_PROPERTY宏,這樣比較麻煩,需要在構造函數中對每個屬性調用一下,希望有更好辦法的朋友告知.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章