===========================================
all of them are inline and public.
a place for invocation of the ‘constructors’ of its base-class and non-static members.(quite similar to destructors)
aside from what we have said in the “constructors”, ‘destructor’ has virtual-or-not distribution.
one-by-one call the copy cosntructors or copy assignment operators.
if any of the functions are prohibited, the copy would fail.
in fact, the condition is not too hard to get.
About second point: (a small class for example), pointers in virtual table means a great explosion about its cost, but in fact, its performance didn’t improve at all. (this rule is quite different from that in the ‘const
’ problem. )
If the destructing process goes wrong, we will then be put into a dilemma:
especially when the exception piles. it’ll then ambiguous for us to catch and debug.
std::abort
to forstall undefined behaviorexample in book’s page 47:
class DBConn
{
void close();//for client, to whom we give a chance to remedy in advance.
{
db.close();
closed = true;
}
~DBConn()//if client look down upon us, huh! GO OUT AND DEBUG!
{
if (!closed)
try{
db.close();
}
cathc(...){
//make log entry
}
}
}
sum-up:: If class clients need to be able to react to exceptions thrown during an operation, the class should provide a regular(i.e., non-destructor) function that performs the operation.(NOT IN DESTRUCTOR)
A little thought, and we’ll find the reason behind the title:
constructors are sometimes have much in common, it’s a good engineering to encapsule the common parts in one
init()
function.
sum-up: To behave polymorphically(log where the base class is derived and constructed) in the base-class’s con/de, we cannot call from the base-side, but there is another way to compensate. We could pass the parameters from sons to base.
*this
it’s just a convention. By returning a reference, we may use assignment chain like this:
x = (y = (z = 15));
This convention applies to all assignment operators, not just the standard form shown above. Hence:
class Widget {
public:
...
Widget& operator+=(const Widget& rhs // the convention applies to
{ // +=, -=, *=, etc.
... // it applies even if the
return *this; // operator's parameter type
} // is unconventional
not allowing the convention, it won’t CE, but it’s been followed widely by built-in types and STL.
operator=
It’s not illegal, so be careful: though obvious assignment is not that probable.
references or pointers to multiple objects of the same type needs to consider that the objects might be the same.
In a polymorphic hierarchy, same type is not even necessary.
// unsafe impl. of operator=
Widget& Widget::operator=(const Widget& rhs)
{
delete pb; // stop using current bitmap
pb = new Bitmap(*rhs.pb); // start using a copy of rhs's bitmap
return *this;
}
When *this
and rhs are the same object, the delete
destroys the bitmap of both.
There are two problems to solve: assign-to-self safety and exception safety
Widget& Widget::operator=(const Widget& rhs)
{
if (this == &rhs) return *this; // identity test: if a self-assignment, do nothing
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
It solves our concerns about self-assignment safety, but is still exception-unsafe.
we just have to be careful not to delete pb until after we’ve copied what it points to:
Widget& Widget::operator=(const Widget& rhs)
{
Bitmap *pOrig = pb; // remember original pb
pb = new Bitmap(*rhs.pb); // make pb point to a copy of *pb
delete pOrig; // delete the original pb
return *this;
}
Now
HOWEVER, exception-safety always renders self-assignment safety, thus, we have the:
Widget& Widget::operator=(const Widget& rhs)
{
Widget temp(rhs); // make a copy of rhs's data
swap(temp); // swap *this's data with the copy's
return *this;
}
when pass vals, we have a itself-temporary technique: by-value, we can even simplify the operator as:
Widget& Widget::operator=(Widget rhs) // rhs is a copy of the object
{ // passed in note pass by val
swap(rhs); // swap *this's data with
// the copy's
return *this;
}
whether a copy constructor or copy assignment operator (they’re all called copying functions)
When you declare your own copying functions, you are indicating to compilers that there is something about the default implementations you don’t like (this is truly an interesting point which indicate why we’re allowed to customized our copying functions). Compilers seem to take offense at this, and they retaliate in a curious fashion: they don’t tell you when your implementations are almost certainly wrong.
just need to remember not to omit anyone
a possible version of invoking base copying functions.
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
: Customer(rhs), // invoke base class copy ctor
priority(rhs.priority)
{
logCall("PriorityCustomer copy constructor");
}
be careful to how we invoke the base-copy
PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
logCall("PriorityCustomer copy assignment operator");
Customer::operator=(rhs); // assign base class
parts
priority = rhs.priority;
return *this;
}
init()
the two copying functions always have lots of similarities.
But they’re essentially compatible. One is used for already-existed obj, another is for obj hasn’t.
So when reusing, we need to encapsule the common parts into a private function (which is conventionally) named init()
e.g. in this case, however, we only have a line common, thus unnecessary to declare one.