P: 属性,__property 关键字
从使用者的角度来看,属性就像是类的成员变量,可以给这个变量赋值,也可以读出变量的值,
但是属性的内部,可以对应读写的处理函数,对属性赋值,会调用写入属性的函数,读出属性值,会调用读取属性的函数。
__property type PropertyName = { attributes };
__property type PropertyName[type1] = { attributes };
__property type PropertyName[type1][type2] = { attributes };
__property PropertyName;
__property PropertyName = { attributes };
项目 |
描述 |
type |
变量类型。从使用者的角度看,属性就像是类的成员变量,这是这个变量的变量类型。
如果不写类型,那么这个属性必须是类继承过来的,这个属性和父类的同名属性的类型相同。 |
PropertyName |
属性名称。从使用者的角度看,属性就像是类的成员变量,这是这个变量的变量名。 |
type1, type2 |
数组属性的下标类型,允许任何有效的变量类型作为数组的下标。 |
attributes |
属性的参数,包含以下表格里面的至少一个项目,每个项目之间用英文标点逗号 “,” 隔开 |
属性的参数 |
描述 |
read = FGet |
FGet 为成员变量或成员函数,读取属性,就相当于读取这个成员变量或调用这个成员函数。
• 如果 FGet 是成员变量,必须和属性的变量类型相同,即上面表格里面的 type 类型的:
type FGet;
• 如果 FGet 是成员函数,返回值必须和属性的变量类型相同,如果是数组属性,参数必须是 type1, type2 类型的:
type FGet(void);
type FGet(type1 p1);
type FGet(type1 p1, type2 p2); |
write = FSet |
FSet 为成员变量或成员函数,写入属性,就相当于写入这个成员变量或调用这个成员函数。
• 如果 FSet 是成员变量,必须和属性的变量类型相同,即上面表格里面的 type 类型的:
type FSet;
• 如果 FSet 是成员函数,参数必须和属性的变量类型相同,数组属性要先写下标类型的参数,后写属性值参数:
void FSet(type v);
void FSet(type1 p1, type v);
void FSet(type1 p1, type2 p2, type v); |
index = v |
v 必须是整数常数值
一般是把数组属性当中的一个元素作为独立的属性使用的,v 值为这个元素在数组里面的下标值,例如:
public:
__property float Value[int] = { read = FGetValue , write = FSetValue };
__property float ValueA = { read = FGetValue , write = FSetValue , index = 0 };
__property float ValueB = { read = FGetValue , write = FSetValue , index = 1 };
private:
float FGetValue(int i);
void FSetValue(int i, float v);
ValueA 就相当于 Value[0]; ValueB 就相当于 Value[1];
a = ValueA 就相当于 a = FGetValue(0); ValueB = b 就相当于 FSetValue(1, b); |
stored = v |
v 必须是 true 或 false。默认值是 true,如果 attributes 里面没有包含这个参数,认为这个参数的值为 true.
• 只有这个类是一个控件的时候,这个参数才有用,是这个控件的这个属性的值是否保存到 .dfm 或 .fmx 文件里面;
• 只有从 TObject 继承过来的类才可以包含这个参数,否则认为语法错误(无法生成控件,这参数就没有意义)。 |
default = v |
v 是和 type 相同类型的常数,是这个属性的默认值。
• 只有这个类是一个控件的时候,这个参数才有用:
· 如果属性值等于 v,这个属性值不储存在 .dfm 或 .fmx 文件里面,而使用内定的默认值,
如果这个类对这个属性初始化的默认值和 v 值不同,会引起错误的结果;
· 如果属性值不等于 v,这个属性的值储存在 .dfm 或 .fmx 文件里面;
• 只有从 TObject 继承过来的类才可以包含这个参数,否则认为语法错误(无法生成控件,这参数就没有意义)。 |
nodefault |
没有默认值,和 stored = true 的功能差不多,如果没有默认值,那么这个属性值会储存到 .dfm 或 .fmx 文件里面。
• 只有这个类是一个控件的时候,这个参数才有用;
• 只有从 TObject 继承过来的类才可以包含这个参数,否则认为语法错误(无法生成控件,这参数就没有意义)。 |
属性的例子:
class MyClassA
{
public:
__property int Number = { read = FNumber };
__property float Value = { read = FGetValue, write = FSetValue };
private:
int FNumber;
float FGetValue(void);
void FSetValue(float v);
}; |
属性 Number 和 Value:
MyClassA a;
a.Number 在类的外部访问就是一个只读的数值了,在类的内部只能通过 FNumber 来改变这个属性的值
float f = a.Value; 执行的是 f = a.FGetValue();
a.Value = f; 执行的是 a.FSetValue(f); |
Copyright © Victor Chen, http://www.cppfans.com/
M: 方法,类的成员函数
在 C++ Builder 里面,类的成员函数称为 PME 模型的 “方法”。
这些成员函数就和普通的类的成员函数一样,没有什么区别。
E: 事件,__closure 关键字
使用 __closure 描述的函数指针是指向类的成员函数的指针,例如:
void (__closure *lpFunction)(String s);
类的成员函数指针,即 __closure 指针作为类的方法的回调函数,这些 __closure 指针用 __property 声明为属性,就称为 “事件”。
例如:
TClassA 里面的
__property void (__closure *MyEvent)(String s) = { read = lpFunction, write = lpFunction };
__closure 是修饰指针的,例如:
void __fastcall (__closure *lpfnMyEvent)(TObject *Sender); // 正确
void __fastcall __closure (*lpfnMyEvent)(TObject *Sender); // 错误
void (__fastcall __closure *lpfnMyEvent)(TObject *Sender); // 错误
class TClassA
{
private:
void (__closure *lpFunction)(String s);
public:
__property void (__closure *MyEvent)(String s) = { read = lpFunction, write = lpFunction };
TClassA();
void MyFunc(String s);
};
TClassA::TClassA()
{
lpFunction = NULL;
}
void TClassA::MyFunc(String s)
{
if(lpFunction)
lpFunction(s);
} |
TClassA 里面的 MyEvent,可以指向 TClassB 定义的对象的 FuncB 函数
class TClassB
{
public:
void FuncB(String s);
};
void TClassB::FuncB(String s)
{
ShowMessage(L"TClassB::FuncB: "+s);
} |
下面就是一段测试代码,执行 A.MyFunc(L"Hello!"); 执行了 B.FuncB(L"Hello!");
执行结果就是:弹出了显示 L"TClassB::FuncB: Hello!" 的信息提示框。
TClassA A;
TClassB B;
A.MyEvent = B.FuncB;
A.MyFunc(L"Hello!"); |
|