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