如何使用TMemoryStream和TStringStream正确检索对象的内部对象

时间:2016-06-13 16:15:35

标签: c++builder c++builder-xe

我有两个名为女人和男人的课程。他们已注册流媒体系统。女人类有一些属性,最重要的是一个类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;
}

1 个答案:

答案 0 :(得分:0)

这与我在my answer earlier question中描述的类似问题相关 - 也就是说,您的Woman类正在定义具有自定义的构造函数参数,因此DFM流系统在从DFM流中读取Woman时无法调用该构造函数。这就是你的Man*指针为NULL的原因 - 你的构造函数没有被调用来初始化那个指针。

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组件并在Object Inspector中设置其属性和子属性,则需要这样做。 DFM流系统还使用csSubComponent设置/清除的内部SetSubComponent()标志。这是向VCL系统发出的信号,fManInClass对象由Woman对象拥有并具有特殊处理。

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

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