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

C++ 中的属性

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.31/5 (15投票s)

2006年12月20日

CPOL

4分钟阅读

viewsIcon

53918

downloadIcon

343

在 C++ 中模拟 C# 属性。

引言

当我用 C# 编写属性时,我想知道为什么 C++ 没有这样的功能?这是一个具有挑战性的问题,因为我知道这对 C++ 开发者来说有多么重要,我花了三天时间来撰写这篇文章。我希望您会觉得它有用,就像我所想的一样。

什么是属性

属性就像是存储数据的变量,但它们会触发在读取或写入数据时触发的事件。换句话说,属性是一个交互式变量,它在读取和写入时评估自身并具有不同的值。

使用 C# 这样的语言编写包含属性的类很容易,但在 C++ 中似乎不可能。这是因为 C++ 编译器不支持像 C# 那样的属性。因此,我写了这篇文章来向您展示如何编写像 C# 那样的属性的 C++ 类。

本文档将向您展示如何使用编写的宏来声明和实现属性。如果您是 C++ 专家,并且需要理解它是如何工作的,那么阅读 "Properties.h" 中定义的宏将不会有任何问题。

为什么属性很重要

让我们想象一下,我们需要编写一个代表人的对象。这个对象可能包含数据,例如

  • 全称
  • 年龄
  • 出生年份
  • 性别

在 C++ 中可以像下面这样编写

class Person {
public:
    Person( ){}
    virtual ~Person( ){}

private://data members

    char m_fName[20];
    char m_lName[20];

    UINT m_YearOfBirth;
    bool m_bGender;
};

注意:在大多数情况下,我们不能将数据成员定义为 public 以便直接使用,因为数据成员应该通过对象实现的业务逻辑来维护。

现在,如果我们想获取或设置 m_bGender 的值,我们需要实现如下所示的方法

class Person {
public:
    Person( ){}
    virtual ~Person( ){}
    
    void SetGender(bool bGender) {m_bGender = bGender;}
    bool GetGender() {return m_bGender;}

private://data members

    char m_fName[20];
    char m_lName[20];

    UINT m_YearOfBirth;
    bool m_bGender;
};

使用此方法的缺点是,您需要知道使用哪个方法来更改 Gender。但是有了属性,生活就更美好了,因为要做同样的事情,您只需要知道属性的名称即可。此外,单个属性可以支持不同的数据类型,换句话说,在我们的示例中,我们可以让 Gender 接受 string 和布尔值,并像下面这样使用它。

Person.Gender = "Male";

Person.Gender = true;

当然,现在有了属性,生活变得更加轻松,代码也变得更具可读性。

属性声明

现在我将向您展示如何编写属性。让我们从编写 Gender 属性开始,如下所示

class Person {
public:
    Person( ){}
    virtual ~Person( ){}
    
    Begin_Property(char*,Gender)
        __get(char*,Gender)
        _set(char*);

        _get(bool);
        _set(bool);

        __release(Gender)
    End_Property(Gender)

private://data members

    char m_fName[20];
    char m_lName[20];

    UINT m_YearOfBirth;
    bool m_bGender;
};

现在让我们看一下代码。我从使用 Begin_Property 宏定义属性开始,该宏接受两个参数:属性数据类型和属性名称。因为 Gender 属性是 string 属性,所以它应该是 char*。在开始定义我的属性后,我需要声明 get(ers)set(ers) 事件,它们将在每次使用时触发,如下所示

Person.Gender = true; // This will fire _set(bool) event
bool gender = Person.Gender ; // this will fire _get(bool) event
Person.Gender = "Male"; //This will fire _set(char*)
printf("Gender :%s\n",(char*)Person.Gender); //This will fire _get(char*)

_get_set 是两个宏,它们接受一个参数,代表属性可以接受的数据类型。您会注意到 _set_get 事件的数据类型独立于属性的数据类型。换句话说,尽管 "Gender" 属性的数据类型是 char*,但它具有 bool 类型的 getter 和 setter。这样,它就可以像上面那样接受布尔值或 string 值。

我使用的最后两个宏是 _release 用于释放其分配的内存(稍后将详细介绍)和 End_Property 用于关闭属性声明,这两个宏都以属性名称作为参数。

属性实现

声明属性后,我们需要实现 set(ers) 和 get(ers)。我们可以在同一位置进行,如下所示

    .
    .
    .
    
    Begin_Property(char*,Gender)
        __get(char*,Gender)
        _set(char*)
        {
           //do something hear 
           return Gender;
        }

        _get(bool)
        {
          //do something hear 
          return iValue;
        }
        _set(bool)
        {
          //do something hear 
          return iValue;
        }

        __release(Gender)
    End_Property(Gender)
    .
    .
    .

或者使用实现宏来实现,但在向您展示如何使用这些宏之前,让我们讨论另外两点:__get 宏是什么?以及为什么 set(ers) 应该返回值?好了,我们在 _get(char*) 中所需要做的就是返回一个指向它的指针,这样 "return Gender;",这就是 __get 所做的。__get 是属性的默认 getter,因为 "Gender" 是一个 char* 属性,所以我们这样使用它 "__get(char*,Gender)"。

让我们转向第二个问题,为什么 set(ers) 应该像 get(ers) 一样返回值?简单来说,在 C++ 中,set(ers) 可以像 get(ers) 一样工作,如下所示

bool bGender = Person.Gender = true;

现在让我们使用 Imp_setImp_get 宏来实现 Gender 属性。这两个宏都接受三个参数:数据类型、类名和属性名,如下所示

Imp_set(char*,Person,Gender)
{
  PROPERTY_PROLOGUE(Person,Gender)
            
  if (!Gender) Gender = new char[7];

  if (strlen(iValue)<6 )
  {     
    int result;
    if ((result=strcmp(iValue,"Male"))==0)
    pThis->m_bGender = true;
    else
    {
      if ((result=strcmp(iValue,"Female"))==0)
        pThis->m_bGender = false;
    }
    
    if(result==0) strcpy(Gender,iValue);
  }
  return Gender;
}

Imp_set(bool,Person,Gender)
{
  PROPERTY_PROLOGUE(Person,Gender)

  if (!Gender) Gender = new char[7];

  if (pThis->m_bGender = iValue)
    strcpy(Gender,"Male");
  else
    strcpy(Gender,"Female");
  
  return (bool)iValue;
}

Imp_get(bool,Person,Gender)
{
  PROPERTY_PROLOGUE(Person,Gender)
  return pThis->m_bGender;
}

由于 set(ers) 和 get(ers) 无法直接访问类成员,因此我们使用了 PROPERTY_PROLOGUE 宏。它定义了一个指向属性类的指针,如上所示,称为 "pThis"。

同样在类终止时,我们需要释放属性使用的内存和任何分配的资源,这就是 __release 宏的函数。__release 是一个默认的 release 宏,它的工作已完成,如下所示

  if (Gender) 
  {
    delete Gender;
    Gender = NULL;
  }

我们也可以使用 _releaseImp_release 自己实现 release 事件,如下所示

Imp_release(Person,Gender)
{
// Release allocated resources here.
}

演示项目

本项目演示了如何编写属性以及属性之间如何相互影响。例如,在 person 类中,YearOfBirth 属性会改变 Age 值属性,反之亦然。您还可以使用类作为属性数据类型,例如,我使用 CTime 作为 "LeaveDate" 和 "ContractDate" 属性的数据类型。

最后……我希望我已经尽我所能地涵盖了这个问题。如有任何疑问,请随时与我联系。非常感谢。

历史

  • 2006 年 12 月 20 日:初始帖子
© . All rights reserved.