首页  »   C++

对象属性的C++兑现

网友分享于:2013-08-15  浏览:11次
对象属性的C++实现

  高级语言的面向对象编程模型一般都支持“属性”接口,例如 Visual Basic 和 C#。在C#中,通过访问属性,不仅可以存取数据,还可以在存取时执行其它操作,在代码形式上,改变属性值可以写成给变量赋值的形式,例如:Form1.Size = System.Drawing.Size(640,480),而不是调用函数:Form1.SetSize ...(实际上Form没有SetSize函数)。C++语言不支持对象属性,对象的可执行接口只有函数,所以C++里没有“属性”和“方法”的概念。但是通过一些编程的技巧,可以实现一个属性访问器,使 C++ 代码可以具有和 C# 相似的编程风格。

属性访问器模板

  代码如下:

template<typename TObject, typename TProperty>

struct PropAccessor {     TObject* Instance;     PropAccessor(TObject* instance): Instance(instance){}     operator TProperty();     TProperty& operator = (TProperty value); };

  其中:operator TProperty() 用来实现属性的"get"部分,TProperty& operator=(TProperty value) 用来实现属性的"set"部分。

  这个模板是不能直接使用的,两个运算符函数都没有实现,为了可以使用,需要继承模板并实现这两个运算符。这样就定义了一个模拟的“属性”。

属性访问器的实现

  下面是一个具体的实现例子。MeshNode是用于3D场景的模型对象,float3是一个向量类型,表示Mesh在场景中的位置,float4x4是一个矩阵类型,表示Mesh的变换矩阵。演示的代码是MeshNode中的Position属性。

union float3 //一个简单的向量类型。
{
    float m[3];
    struct
    {
        float x, y, z;
    };
    float3()
    {
        for(int i = 0; i < 3; i++)
        {
            this->m[i] = 0.0f;
        }
    };
};
union float4x4 //一个简单的矩阵类型。
{
    float m[16];
    struct
    {
        float _11, _12, _13, _14;
        float _21, _22, _23, _24;
        float _31, _32, _33, _34;
        float _41, _42, _43, _44;
    };
    float4x4()
    {
        for(int i = 0; i < 16; i++)
        {
            this->m[i] = 0.0f;
        }
    };
}; 
class MeshNode
{
public:
    //属性访问器的实现类。这个类直接嵌套在MeshNode,用来实现属性"Position".
    class PROP_Position: public PropAccessor<MeshNode, float3>
    {
        friend class MeshNode;//这个声明使MeshNode可以创建PROP_Position对象,而外部调用者不能创建PROP_Position,因为下面的构造函数是私有。
    private:
        PROP_Position(MeshNode* instance): PropAccessor(instance){};
    public:
        //Property "get":实现了模版中对应的运算符,TProperty被替换为float3。这里只是简单的取得数据,也可以做任何其它操作。
        operator float3()
        {
            float3 rv;
            rv.x = this->Instance->m_WorldMatrix._41;
            rv.y = this->Instance->m_WorldMatrix._42;
            rv.z = this->Instance->m_WorldMatrix._43;
            return rv;
        };
        //Property "set":实现了模板中对应的运算符,TProperty被替换为float3。这里只是简单的保存数据,也可以做任何其它操作。
        float3& operator=(const float3& value)
        {
            this->Instance->m_WorldMatrix._41 = value.x;
            this->Instance->m_WorldMatrix._42 = value.y;
            this->Instance->m_WorldMatrix._43 = value.z;
            return value;
        };
    };
//
private:
    //
    //...
    float4x4 m_WorldMatrix;
    //
public:
    //
    //...
    //

    //属性"Position":取得/设置对象的位置。(前面"PROP_Position"的一堆代码就为了实现这个).
    PROP_Position Position()
    {
        return PROP_Position(this);
    };
};

属性访问器的使用

  有了这些代码,就可以使用"Position"属性了,MeshNode中的Position函数返回的是一个PROP_Position对象,通过这个对象的运算符使代码具有使用属性的风格。例如:

MeshNode* my_mesh = new MeshNode();
float3 my_pos;
my_pos.x = 10.0f;
my_pos.y = 10.0f;
my_pos.z = 10.0f;
my_mesh->Position() = my_pos; //有了使用属性的代码形式。这时调用了"MeshNode::PROP_Position::operator=()"函数。
float3 world_pos = my_mesh->Position(); //和普通C++函数形式相同,但是用到了属性访问器,这时调用了"MeshNode::PROP_Position::operator float3()"函数。

简单总结

  这个属性访问器的优点就不用多说了,主要讨论缺点。具体开发中,可以根据具体情况来决定是否使用它。

  1. 增加了代码量。需要编写多余的代码,为每个属性接口实现一个访问器。

  2. 和传统的"getPosition"和"setPosition"函数相比,通过属性访问器对数据访问,效率略有下降。在"get"过程中,额外的操作是:创建并返回一个访问器对象,调用运算符"operator TProperty()";在"set"过程中,额外的操作是:创建并返回一个访问器对象,调用运算符"TProperty& operator = (TProperty)",且属性值多了一次引用传递。

  3.不能被重载。这样实现的属性不能被重载,如果需要重载,需要更复杂的实现方法。如果不想折腾的话,在程序的设计中就要尽量避免重载属性。

  总体来说对性能影响不大,在不苛刻要求效率的情况下,这些额外的开销可以忽略。只是增加了实现属性访问器的开发工作,如果希望属性可以被重载,需要更多的技巧和工作量,实现方法其实也没有难度,大家可以自由发挥。

相关解决方案

最新解决方案