s
[ Pobierz całość w formacie PDF ]
-- are augmented by the compiler, which inserts additional code into them to invoke the
copy constructors of direct base classes and embedded objects. It is guaranteed, however,
that virtual base subobjects are copied only once.
Simulating Virtual Constructors
Unlike ordinary member functions, a constructor has to know the exact type of its object
at compile time in order to construct it properly. Consequently, a constructor cannot be
declared virtual. Still, creating an object without knowing its exact type is useful in
certain conditions. The easiest way to simulate virtual construction is by defining a
virtual member function that returns a constructed object of its class type. For example
class Browser
{
public:
Browser();
Browser( const Browser&);
virtual Browser* construct()
{ return new Browser; } //virtual default constructor
virtual Browser* clone()
{ return new Browser(*this); } //virtual copy constructor
virtual ~Browser();
//...
};
class HTMLEditor: public Browser
{
public:
HTMLEditor ();
HTMLEditor (const HTMLEditor &);
HTMLEditor * construct()
{ return new HTMLEditor; }//virtual default constructor
HTMLEditor * clone()
{ return new HTMLEditor (*this); } //virtual copy constructor
virtual ~HTMLEditor();
//...
};
The polymorphic behavior of the member functions clone() and construct() enables -
you to instantiate a new object of the right type, without having to know the exact type of
the source object.
void create (Browser& br)
{
br.view();
Browser* pbr = br.construct();
//...use pbr and br
delete pbr;
}
pbr is assigned a pointer to an object of the right type -- either Browser or any class
publicly derived from it. Note that the object br does not delete the new object it has
created; this is the user's responsibility. If it did, the lifetime of the reproduced objects
would depend on the lifetime of their originator -- which would significantly compromise
the usability of this technique.
Covariance of Virtual Member Functions
The implementation of virtual constructors relies on a recent modification to C++,
namely virtual functions' covariance. An overriding virtual function has to match the
signature and the return type of the function it overrides. This restriction was recently
relaxed to enable the return type of an overriding virtual function to co-vary with its class
type. Thus, the return type of a public base can be changed to the type of a derived class.
The covariance applies only to pointers and references.
CAUTION: Please note that some compilers do not support virtual
member functions' covariance yet.
Assignment Operator
A user-declared assignment operator of class C is a nonstatic, nontemplate member
function of its class, taking exactly one argument of type C, C&, const C&, volatile C&,
or const volatile C&.
Implicitly-Defined Assignment Operator
If there is no user-defined assignment operator for a class, the implementation implicitly
declares one. An implicitly-declared assignment operator is an inline public member
of its class, and it has the form
C& C::operator=(const C&);
if each base class of C has an assignment operator whose first argument is a reference to a
const object of base class type, and if all the nonstatic embedded objects in C also have
an assignment operator that takes a reference to a const object of their type. Otherwise,
the implicitly-declared assignment operator is of the following type:
C& C::operator=(C&);
An implicitly-declared assignment operator has an exception specification. The exception
specification contains all the exceptions that might be thrown by other special functions
that the assignment operator invokes directly. An assignment operator is said to be trivial
if it is implicitly declared, if its class has no virtual member functions or virtual base
classes, and if its direct base classes and embedded objects have a trivial assignment
operator.
Simulating Inheritance Of Assignment Operator
Because an assignment operator is implicitly declared for a class if it is not declared by
the programmer, the assignment operator of a base class is always hidden by the
assignment operator of a derived class. In order to extend -- rather than override -- the
assignment operator in a derived class, you must first invoke the assignment operator of
the base explicitly, and then add the operations that are required for the derived class. For
example
class B
{
private:
char *p;
public:
enum {size = 10};
const char * Getp() const {return p;}
B() : p ( new char [size] ) {}
B& operator = (const C& other);
{
if (this != &other)
strcpy(p, other.Getp() );
return *this;
}
};
class D : public B
{
private:
char *q;
public:
const char * Getq() const {return q;}
D(): q ( new char [size] ) {}
D& operator = (const D& other)
{
if (this != &other)
{
B::operator=(other); //first invoke base's assignment operator
explicitly
strcpy(q, (other.Getq())); //add extensions here
}
return *this;
}
};
When Are User-Written Copy Constructors And
Assignment Operators Needed?
The synthesized copy constructor and assignment operator perform a memberwise copy.
This is the desirable behavior for most uses. However, it can be disastrous for classes that
contain pointers, references, or handles. In such cases, you have to define a copy
[ Pobierz całość w formacie PDF ]