65.9K
CodeProject 正在变化。 阅读更多。
Home

C++ 中属性的实现

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.56/5 (46投票s)

2003 年 4 月 3 日

4分钟阅读

viewsIcon

218143

downloadIcon

1052

在 c++ 对象上实现属性

引言

这个项目尝试在 C++ 中模拟 C#(和其他语言...)中存在的属性行为,而不使用任何扩展。 大多数在 C++ 中实现属性的库或编译器都使用扩展,例如 Managed C++ 或 C++ Builder,或者他们使用看起来像普通方法而不是属性的 setget 方法。

让我们首先看看什么是属性。 从库用户的角度来看,属性的行为类似于字段或成员变量,但它通过 readwrite 方法或 setget 方法访问底层变量。

例如,我有 class A 和属性 Count,我可以编写以下代码。

A foo;
    
cout <<  foo.Count;

Count 属性实际上调用 get 函数来返回所需变量的值。 使用属性而不是直接访问变量的主要优点之一是你可以将属性设置为 只读(你只能读取变量,不能更改它)、只写读写,所以让我们实现它。

我们需要能够做到以下几点。

//-- Will call the get function to get the underlying value
int i = foo.Count;

//-- Will call the set function to set the value
foo.Count = i;

因此,很明显我们需要重载 = 运算符来设置变量,以及返回类型(我们稍后会展示)。

我们将实现一个名为 property 的类,它将完全像一个属性一样工作,如下面的示例所示。

template<typename Container, typename ValueType, int nPropType>
    class property {}

这个模板类将代表我们的属性。 Container 是包含变量、set & get 方法和属性的类的类型。 ValueType 是内部变量本身的类型。 nPropType 是属性的访问类型(只读只写读写)。

所以现在我们需要从容器类设置一个指向 setget 方法的指针到属性,并且还要重写运算符 =,以便该属性的行为类似于变量,所以让我们看看属性类的完整列表。

#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;
    }

    //-- 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 = operator 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;
    }

    //-- Cast the property class to the internal type --
    operator ValueType()
    {
        assert(m_cObject != NULL);
        assert(Get != NULL);
        return (m_cObject->*Get)();
    }

private:
    //-- Pointer to the module that contains the property --
    Container* m_cObject;
    //-- Pointer to set member function --
    void (Container::*Set)(ValueType value);
    //-- Pointer to get member function --
    ValueType (Container::*Get)();};

现在让我们检查每一部分。

以下代码只是将 Container 指针设置为包含我们将要访问的属性的有效实例。

void setContainer(Container* cObject)
{
    m_cObject = cObject;
}

以下代码设置指向容器类的 getset 成员函数的指针。唯一的限制是 set 成员函数必须接受一个参数并返回 void,而 get 成员函数必须不接受参数但返回该值类型。

//-- 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 成员并传递该值。 第二个运算符使整个属性类充当 ValueType,因此它返回由 get 函数返回的值。

//-- Overload the = operator 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)();
}

现在让我们看看如何使用它。

如下所示,PropTest 类实现了一个名为 Count 的简单属性。 实际值将通过 getset 方法存储并从私有成员变量 m_nCount 中检索。 只要它们的地址被传递给属性类,如 PropTest 对象的构造函数中所示,getset 方法可以具有任何名称。 property<PropTest,int,READ_WRITE> Count; 行表示我们在名为 CountPropTest 类中有一个类型为 integer读写 属性。 现在你可以像调用普通成员变量一样调用 Count,即使你实际上是在间接调用 setget 方法。

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;

注意:如果你将属性设置为只读并尝试为其赋值,这将在调试版本中导致断言。 同样,如果你将属性设置为只写并尝试读取它,则会在调试版本中发生断言。

结论

这展示了如何在 C++ 中使用标准 C++ 实现属性,而无需任何扩展。 当然,使用 setget 方法更有效,因为使用此方法,每个属性都有一个属性类的新实例。

© . All rights reserved.