C++ Builder 的 PME 架构

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: 里面的都是属性和事件,这些属性和事件安装在控件的属性页面和事件页面上。

控件的属性页面
控件的事件页面

相关:


C++ Builder 参考手册C++ Builder 的 PME 架构

推荐阅读更多精彩内容