I have 2 classes named Woman and Man. They have been registered for streaming system. Woman class has some attributes and most importantly an instance of class Man in it. Using TMemoryStream and TStringStream classes I was able to retrieve all attributes of Woman but Man*, by WriteComponent and ReadComponent methods of TmemoryStream class. Actually the compiler throws an exception and the reason is that Man* is NULL and is not loaded properly. In my program I need to load all attributes including simple data types and instances of other written classes. Please give me advice how to load Woman object properly so that Man* is not NULL any more. here is my code snippet.
#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;
}
This is related to a similar problem I described in my answer to your earlier question - namely, that your
Womanclass is defining a constructor that has custom parameters, and as such the DFM streaming system cannot call that constructor when reading aWomanfrom a DFM stream. That is why yourMan*pointer is NULL - your constructor is not being called to initialize that pointer.There is only one constructor signature that the DFM can call:
You cannot change that signature. You can define additional overloaded constructors with extra parameters as needed, but the above constructor is required for DFM streaming.
Try this instead:
Also notice the extra calls to
fManInClass->SetSubComponent(). That is needed if you ever want to use yourWomancomponent in the Form Designer at design-time and set its properties and sub-properties in the Object Inspector. The DFM streaming system also makes use of the internalcsSubComponentflag thatSetSubComponent()sets/clears. This is a signal to the VCL system that thefManInClassobject is owned by theWomanobject and has special handling.Also notice the setter method added for the
ManInClassproperty. SinceWomanowns theManobject, you don't want to allow an outside caller to change the value of thefManInClassvariable, thus causing a memory leak and ownership conflicts. A published property needs to be read/write in order to be DFM-streamable (unless you override the virtualTComponent::DefineProperties()method to provide custom streaming), so it needs both a getter and a setter, but you can use class methods to protect access to that variable.I strongly suggest you get yourself a good book on how to write VCL components.