class X : public BASE {
M m; // a member class instance
M* p; // a member pointer to a class instance
int i; // a fundamental type
// ...
};
Previous releases implement X::storer() and X::X(istream&,X&) as follows:
void X::storer(ostream& strm)
{
BASE::storer(strm);
m.storeOn(strm);
p->storeOn(strm);
strm « i « " ";
}
void X::X(istream&strm, X& where)
: (strm,where)
{
this = &where;
readFrom(strm,"M",m);
p = (M*)readFrom(strm,"M");
strm » i;
}
void X::storer(FileDescTy& fd)
{
BASE::storer(fd);
m.storeOn(fd);
p->storeOn(fd);
storeBin(fd,i);
}
void X::X(FileDescTy& fd, X& where)
: (fd,where)
{
this = &where;
readFrom(fd,"M",m);
p = (M*)readFrom(fd,"M");
readBin(fd,i);
}
The problem is that this constructor first initializes m using the M::M() constructor, then calls readFrom(), which overwrites this initialized instance with an instance constructed by reading strm. We didn't notice this bug earlier because, in practice, the problem occurs only in classes Rectangle and SharedQueue, and has no obvious consequences. The worst that is likely to happen is that M::M() allocates some memory that never gets reclaimed.
Unfortunately, the fix requires some widespread changes. But, it turns out that numerous other improvements become possible. The new format for storer() functions and readFrom() constructors when not using MI is:
void X::storer(OIOout& strm)
{
BASE::storer(strm);
m.storeMemberOn(strm);
p->storeOn(strm);
strm « i;
}
void X::X(OIOin& strm)
: (strm), m(strm)
{
p = M::readFrom(strm);
strm » i;
}
void X::storer(OIOofd& fd)
{
BASE::storer(fd);
m.storeMemberOn(fd);
p->storeOn(fd);
fd « i;
}
void X::X(OIOifd& fd)
: (fd), m(fd)
{
p = M::readFrom(fd);
fd » i;
}
The new format is simpler and consistent—storer() functions always call BASE::storer() and X::X(OIOin&) constructors always call BASE::BASE(OIOin&).