天天看點

給C++添加屬性機制

    以前用DELPHI和C#時,對DELPHI和C#文法中的屬性感到十分友善,在讀寫屬性時能自動調用屬性的get, set函數或代碼.但我最喜歡的C++中沒有屬性機制.不過C++提供了範型程式設計和操作符重載機制,足以讓一切變成可能.

    假定要添加屬性的類是目标類,給C++添加屬性機制,我的想法是建立一個類,重載此類的 "=" 操作符,這樣給這個類指派時,會調用此類的operator = 函數,在此函數中調用目标類的類成員函數即可.但要調用目标類的類成員函數,需要目标類類指針和類成員函數指針.類成員函數指針的類型可以通過模闆傳入.廢話不多說,代碼如下:(VC7中編譯通過,GCC還未測試)

Property.h

#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宏,這樣比較麻煩,需要在構造函數中對每個屬性調用一下,希望有更好辦法的朋友告知.