-1

我有 2 个班级,名为女人和男人。他们已经注册了流媒体系统。Woman 类有一些属性,最重要的是其中有一个 Man 类的实例。使用 TMemoryStream 和 TStringStream 类,我能够通过 TmemoryStream 类的 WriteComponent 和 ReadComponent 方法检索 Woman 但 Man* 的所有属性。实际上编译器会抛出异常,原因是 Man* 为 NULL 并且未正确加载。在我的程序中,我需要加载所有属性,包括简单数据类型和其他编写类的实例。请给我建议如何正确加载 Woman 对象,以便 Man* 不再为 NULL。这是我的代码片段。

#include <vcl.h>
#pragma hdrstop

#include <tchar.h>
#include <memory>
#include <iostream>
#include <conio.h>
#include <string>

#pragma argsused

using namespace std;

class Man : public TComponent
{
    private:
    double fMoney;
    public:
    __fastcall Man(TComponent* _Owner,double InMoney)
        : TComponent(_Owner)
        {
            fMoney = InMoney;
        }
    __published:
    __property double Money = {read=fMoney, write=fMoney};
};

class Woman : public TComponent
{
    private:
    int fAge;
    UnicodeString fMyName;
    Man* fManInClass;
    public:
    __fastcall Woman(TComponent* _Owner, int InAge, UnicodeString InName)
        : TComponent(_Owner)
    {
        fAge = InAge;
        fMyName = InName;
        fManInClass = new Man(this, 0);
    }
    __published:
    __property int Age = {read=fAge, write=fAge};
    __property UnicodeString MyName = {read=fMyName, write=fMyName};
    __property Man* ManInClass = {read = fManInClass, write = fManInClass};
};


void RegisterClassesWithStreamingSystem(void)
{

  #pragma startup RegisterClassesWithStreamingSystem
  Classes::RegisterClass(__classid(Man));
  Classes::RegisterClass(__classid(Woman));
}


int _tmain(int argc, _TCHAR* argv[])
{
    Woman* FirstWoman = new Woman(NULL, 25, "Anjelina");
    FirstWoman->ManInClass->Money = 2000;
    UnicodeString as;
    auto_ptr<TMemoryStream> MStr(new TMemoryStream);
    auto_ptr<TStringStream> SStr(new TStringStream(as));

    MStr->WriteComponent(FirstWoman);
    MStr->Seek(0, soFromBeginning);
    ObjectBinaryToText(MStr.get(), SStr.get());
    SStr->Seek(0, soFromBeginning);
    as = SStr->DataString;

    auto_ptr<TMemoryStream> pms(new TMemoryStream);
    auto_ptr<TStringStream> pss(new TStringStream(as));
    TComponent *pc;

    ObjectTextToBinary(pss.get(), pms.get());
    pms->Seek(0, soFromBeginning);

    pc = pms->ReadComponent(NULL);


    Woman* AWoman = dynamic_cast<Woman*>(pc);

    cout << AWoman->Age << endl;
    cout << AWoman->MyName.c_str() << endl;
    cout << AWoman->ManInClass->Money << endl; // AWoman->ManInClass is NULL -> Exception

    delete FirstWoman;
    pc->Free();
    getch();
    return 0;
}
4

1 回答 1

0

这与我在对您之前的问题的回答中描述的类似问题有关- 即您的类正在定义一个具有自定义参数的构造函数,因此 DFM 流系统在从 DFM 流中读取 a 时无法调用该构造函数。这就是您的指针为 NULL 的原因 - 您的构造函数没有被调用来初始化该指针。WomanWomanMan*

DFM只能调用一个构造函数签名:

__fastcall <classname>(TComponent* Owner)

您无法更改该签名。您可以根据需要使用额外的参数定义额外的重载构造函数,但上述构造函数是 DFM 流式传输所必需的。

试试这个:

#include <vcl.h>
#pragma hdrstop

#include <tchar.h>
#include <memory>
#include <iostream>
#include <conio.h>
#include <string>

#pragma argsused

using namespace std;

class Man : public TComponent
{
private:
    double fMoney;

public:
    __fastcall Man(TComponent* Owner)
        : TComponent(Owner)
    {
    }

    __fastcall Man(TComponent* Owner, double InMoney)
        : TComponent(Owner)
    {
        fMoney = InMoney;
    }

__published:
    __property double Money = {read=fMoney, write=fMoney};
};

class Woman : public TComponent
{
private:
    int fAge;
    UnicodeString fMyName;
    Man* fManInClass;

    void __fastcall SetManInClass(Man *Value)
    {
        fManInClass->Money = Value->Money;
    }

public:
    __fastcall Woman(TComponent* Owner)
        : TComponent(Owner)
    {
        fManInClass = new Man(this);
        fManInClass->SetSubComponent(true);
    }

    __fastcall Woman(TComponent* Owner, int InAge, UnicodeString InName)
        : TComponent(Owner)
    {
        fAge = InAge;
        fMyName = InName;
        fManInClass = new Man(this);
        fManInClass->SetSubComponent(true);
    }

__published:
    __property int Age = {read=fAge, write=fAge};
    __property UnicodeString MyName = {read=fMyName, write=fMyName};
    __property Man* ManInClass = {read = fManInClass, write = SetManInClass};
};

void RegisterClassesWithStreamingSystem(void)
{
  Classes::RegisterClass(__classid(Man));
  Classes::RegisterClass(__classid(Woman));
}
#pragma startup RegisterClassesWithStreamingSystem

int _tmain(int argc, _TCHAR* argv[])
{
    auto_ptr<Woman> FirstWoman(new Woman(NULL, 25, L"Anjelina"));
    FirstWoman->ManInClass->Money = 2000;

    auto_ptr<TMemoryStream> MStr(new TMemoryStream);
    auto_ptr<TStringStream> SStr(new TStringStream(L""));

    MStr->WriteComponent(FirstWoman);
    MStr->Position = 0;
    ObjectBinaryToText(MStr.get(), SStr.get());
    SStr->Position = 0;
    UnicodeString as = SStr->DataString;

    auto_ptr<TMemoryStream> pms(new TMemoryStream);
    auto_ptr<TStringStream> pss(new TStringStream(as));

    ObjectTextToBinary(pss.get(), pms.get());
    pms->Position = 0;

    auto_ptr<TComponent> pc(pms->ReadComponent(NULL));

    Woman* AWoman = static_cast<Woman*>(pc.get());    

    cout << AWoman->Age << endl;
    cout << AWoman->MyName.c_str() << endl;
    cout << AWoman->ManInClass->Money << endl;

    pc.reset();
    FirstWoman.reset();

    getch();
    return 0;
}

还要注意对 的额外调用fManInClass->SetSubComponent()。如果您想Woman在设计时使用表单设计器中的组件并在对象检查器中设置其属性和子属性,则需要这样做。DFM 流系统还利用设置/清除的内部csSubComponent标志。SetSubComponent()这是向 VCL 系统发出的一个信号,表明该fManInClass对象归该Woman对象所有并且具有特殊处理。

ManInClass还要注意为属性添加的 setter 方法。由于Woman拥有该Man对象,您不希望允许外部调用者更改fManInClass变量的值,从而导致内存泄漏和所有权冲突。发布的属性需要读/写才能成为 DFM 流式传输(除非您重写虚拟TComponent::DefineProperties()方法以提供自定义流式传输),因此它需要 getter 和 setter,但您可以使用类方法来保护对它的访问多变的。

我强烈建议你给自己买一本关于如何编写 VCL 组件的好书。

于 2016-06-13T17:21:17.017 回答