C++ 中的属性






3.31/5 (15投票s)
在 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_set
和 Imp_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;
}
我们也可以使用 _release
和 Imp_release
自己实现 release 事件,如下所示
Imp_release(Person,Gender)
{
// Release allocated resources here.
}
演示项目
本项目演示了如何编写属性以及属性之间如何相互影响。例如,在 person
类中,YearOfBirth
属性会改变 Age
值属性,反之亦然。您还可以使用类作为属性数据类型,例如,我使用 CTime
作为 "LeaveDate
" 和 "ContractDate
" 属性的数据类型。
最后……我希望我已经尽我所能地涵盖了这个问题。如有任何疑问,请随时与我联系。非常感谢。
历史
- 2006 年 12 月 20 日:初始帖子