Re: Effective C++ by Scott Meyers
"sks" <sks@yahoo.com> wrote in message
news:LNNDh.45459$5F3.13240@newsfe14.lga...
I didn't quite understand this but I would like to understand it. What did
you mean by "exploit the code generation features of the
compiler"?
If you would have to perform an initialization of a complex object in C
you would have to perform checks if the initialization of a subobject was
successful.
In case of one initialization of some subobject fails you would have to call
the destructor of the already successful initialized subobjects.
You would have to maintain this order in the constructor and in the
destructor. Lets assume 3 parts A, B and C:
(0 return code stands for success, everything else for failure)
typedef struct
{
A sA;
B sB;
C sC;
} someStructure;
int initSomeStructure(someStructure *_p)
{ if (initA(&_p->sA))
return 1;
if (initB(&_p->sB))
{ destroyA(&_p->sA);
return 1;
}
if (initC(&_p->sC))
{ destroyB(&_p->sB);
destroyA(&_p->sA);
return 1;
}
return 0;
}
void destroySomeStructure(someStructure *_p)
{ destroyC(&_p->sC);
destroyB(&_p->sB);
destroyA(&_p->sA);
}
Usage of this code could look like this:
someStructure *p = malloc(sizeof*p);
if (!p)
{ printf("allocation of somestructure failed\n");
}
else
if (initSomeStructure(p))
{ free(p);
printf("Initialization of somestructure failed\n");
}
else
doSomething(p);
ok so far?
in C++ this would look like this:
struct SomeStructure
{ A m_sA;
B m_sB;
C m_sC;
SomeStructure(void)
{
}
};
Usage of this code would look like this:
SomeStructure *p;
try
{ p = new SomeStructure;
} catch (const std::exception &_r)
{ cout << "Initialization or allocation of somestructure failed with
msg: " << _r.why() << "\n";
}
p->doSomething();
The C++ compiler is generating all this code for you (initializaing the
subobjects and dealing with failure if initialization of some subobject
fails).
I only used a heap-allocated object to show that the compiler generates a
call to delete if the constructor fails.
Notice that you enter the same catch-block independ of whether the
constructor fails or whether new fails.
Also, what is wrong with multiple resource allocations inside a
constructor
body (as long as the matching destructor gets to deallocate them)?
if you allocate multiple resources in a single constructor-body
(
e.g. not as subobjects but e.g. pointers.
Same applies if you wrap multiple C-style resources initializations inside a
single class instead of one class for one C-style resource
):
struct A
{ B *m_pB;
C *m_pC;
D *m_pD;
A(void)
{ m_pB = createB();
try
{ m_pC = createC();
} catch (...)
{ delete m_pB;
throw;
}
try
{ m_pD = createD();
} catch (...)
{ delete m_pC;
delete m_pB;
throw;
}
}
~A(void)
{ delete m_pD;
delete m_pC;
delete m_pB:
}
};
Same applies if your creation functions are C-style error code returning
functions.
You would have to perform cleanup duty yourself.
It is better to use sub-objects:
struct A
{ std::auto_ptr<B> m_sB;
std::auto_ptr<C> m_sC;
std::auto_ptr<D> m_sD;
A(void)
: m_sB(createB()),
m_sC(createC()),
m_sD(createD())
{
}
}
Here the compiler generates the code which destroys the already successfully
constructed subobjects
if construction of some subobject fails.
And you don't need to code the destructor at all.
(
std::auto_ptr is an exception to the rule that you should undo in the
destructor what you did in the constructor.
using std::auto_ptr you undo in the destructor what you do just before
calling the constructor.
This is ok, since the constructor of std::auto_ptr never throws.
)
Using C++ Exception Handling it is finally possible to use constructors for
fallible resource allocation.
Write one class for every type of resource allocation you have to perform.
Then chain such classes into base-class or member-class relationships.
Of course you will have to know (and exploit) the order of construction of
base-objects/member-objects.