C++ Builder 参考手册 ➙ C++ Builder 的 PME 架构
__property, __closure, __published, 经常能看到 C++ Builder 这些关键字,都有什么作用呢?
PME 是 property-method-event (属性-方法-事件) 的缩写,这是 C++ Builder 控件实现的基础,__property, __closure, __published 是为了实现控件的属性和事件扩充的 C++ 关键字。属性、方法和事件都是类的成员,其中:
- 属性:就像一个成员变量,读和写这个属性可以对应这个类的两个函数;
- 方法:就是类的成员函数;
- 事件:是回调函数指针,与普通的函数指针不同,这是指向成员函数的指针。
一. 属性 (property)
__property 类型 名称 = { 参数 };
__property 类型 名称[类型1] = { 参数 };
__property 类型 名称[类型1][类型2] = { 参数 };
__property 名称;
__property 名称= { 参数 };
项目 | 说明 |
---|---|
类型 | 属性的变量类型 |
名称 | 属性的名称,相当于变量名 |
类型1,类型2 | 数组属性的下标的变量类型 |
参数 | 包含以下表格里的至少一个参数项,项目间用逗号 "," 隔开 |
参数项 | 说明 |
---|---|
read = FGet | 读取属性,FGet 为成员变量或成员函数 |
write = FSet | 写入属性,FSet 为成员变量或成员函数 |
index = n | 这个属性是一个数组属性中的一个元素,n 是下标值 |
stored = b | 控件:是否把这个属性值存到 .dfm 或 .fmx 里面 |
default = v | 控件:这个属性的默认值 = v |
nodefault | 控件:这个属性没有默认值 |
- 类型:从使用者的角度看,属性就像是类的成员变量,这是这个变量的变量类型。如果不写类型,那么这个属性必须是类继承过来的,这个属性和父类的同名属性的类型相同。
- 名称:从使用者的角度看,属性就像是类的成员变量,这是这个变量的变量名。
- 类型1,类型2:数组属性的下标的变量类型。
- read = FGet | 读取属性,FGet 为成员变量或成员函数
- write = FSet | 写入属性,FSet 为成员变量或成员函数
- index = n | 这个属性是一个数组属性中的一个元素,n 是下标值
- stored = b | 控件:是否把这个属性值存到 .dfm 或 .fmx 里面
- default = v | 控件:这个属性的默认值 = v
- nodefault | 控件:这个属性没有默认值
例1: 让一个变量只读
下面代码里面的 TTest 类的 FValue 值由于放在了 private: 里面,所以在类的外面只能通过 Value 属性读取,用 SetValue 函数赋值。
class TTest
{
private:
int FValue;
public:
__property int Value = { read = FValue };
void SetValue(int i)
{
FValue = i;
}
TTest()
{
FValue = 0;
}
};
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TTest t;
t.SetValue(10);
ShowMessage(t.Value);
}
例2: 属性的读写分别使用两个不同的函数
class TTest1
{
private:
UnicodeString sMyText;
UnicodeString FGetText(void)
{
return sMyText;
}
void FSetText(UnicodeString s)
{
sMyText = s;
}
public:
__property UnicodeString Text = { read = FGetText, write = FSetText };
};
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TTest1 t;
t.Text = L"Hsuanlu";
ShowMessage(t.Text);
}
对于给成员变量来说,这样的属性似乎没有什么实际意义,但是对于控件来说,并不一定是给成员变量赋值,比如给 TEdit 编辑框的 Text 属性赋值,会执行把文字显示在编辑框里面的函数,读取 TEdit 编辑框的 Text 属性值,会从编辑框里面读取文字。
例3:数组属性
class TTest1
{
private:
UnicodeString sMyText;
UnicodeString FGetText(void)
{
return sMyText;
}
void FSetText(UnicodeString s)
{
sMyText = s;
}
wchar_t FGetChar(int iIndex)
{
if(iIndex >= 1 && iIndex <= sMyText.Length())
return sMyText[iIndex];
return 0;
}
void FSetChar(int iIndex, wchar_t c)
{
if(iIndex >= 1 && iIndex <= sMyText.Length())
sMyText[iIndex] = c;
}
public:
__property UnicodeString Text = { read = FGetText, write = FSetText };
__property wchar_t Char[int] = { read = FGetChar, write = FSetChar };
__property wchar_t Ch01 = { read = FGetChar, index = 1 };
__property wchar_t Ch02 = { read = FGetChar, index = 2 };
};
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TTest1 t;
t.Text = L"Hsuanlu";
Memo1->Lines->Add(t.Text);
Memo1->Lines->Add(t.Char[4]);
Memo1->Lines->Add(t.Ch02);
}
二. 方法 (method)
方法是类的成员函数,不需要什么特殊说明。
三. 事件 (event)
事件也是一种属性,属性的类型为 __closure 指针,即指向成员函数的函数指针。
事件是作用是让回调函数使用类的成员函数,而不是全局函数或静态成员。
__closure 关键字是修饰指针的,说明这个指针指向成员函数,例如:
void (__closure *pFunc)(int i);
这里的 (__closure *函数名)
都要写在括号里面。
这样的写法是错误的:void __closure (*pFunc)(int i);
例子:
class TMyControl
{
public:
typedef void __fastcall (__closure *TMyEvent)(TMyControl *Sender, UnicodeString s);
private:
UnicodeString FText;
TMyEvent FEvent;
UnicodeString FGetText(void)
{
return FText;
}
void FSetText(UnicodeString s)
{
FText = s;
}
public:
__property UnicodeString Text = { read = FGetText , write = FSetText };
__property TMyEvent Event = { read = FEvent , write = FEvent };
void __fastcall MyMethod(void)
{
if(FEvent)
FEvent(this, FText);
}
TMyControl()
{
FEvent = NULL;
}
};
class TMyTest
{
private:
void __fastcall MyEvent(TMyControl *Sender, UnicodeString s)
{
ShowMessage(L"TMyTest::MyEvent:\r\n" + s);
}
public:
TMyControl Control;
TMyTest()
{
Control.Event = MyEvent;
}
};
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TMyTest t;
t.Control.Text = L"Hello Hsuanlu!";
t.Control.MyMethod();
}
运行结果:
四. 用于控件类的 __published 关键字
从 TComponent 继承的类可以安装在 C++ Builder 控件面板上,这样的类除了 public:、protected:、private: 之外,还扩充了 __published: ,写在 __published: 里面的都是属性和事件,这些属性和事件安装在控件的属性页面和事件页面上。
相关:
- 枚举控件所有的属性、事件和方法
- 枚举窗口内所有的控件
- System::TObject
C++ Builder 参考手册 ➙ C++ Builder 的 PME 架构