Item 45: Know what functions C++ silently writes and calls.
When is an empty class not an empty class? When C++ gets through with it. If you don't declare them yourself, your thoughtful compilers will declare their own versions of a copy constructor, an assignment operator, a destructor, and a pair of address-of operators. Furthermore, if you don't declare any constructors, they will declare a default constructor for you, too. All these functions will be public. In other words, if you write
class Empty{};
it's the same as if you'd written
class Empty { public: Empty(); // default constructor Empty(const Empty& rhs); // copy constructor
~Empty(); // destructor see // below for whether // it's virtual Empty& operator=(const Empty& rhs); // assignment operator
Empty* operator&(); // address-of operators const Empty* operator&() const; };
Now these functions are generated only if they are needed, but it doesn't take much to need them. The following code will cause each function to be
const Empty e1; // default constructor; // destructor
Empty e2(e1); // copy constructor
e2 = e1; // assignment operator
Empty *pe2 = &e2; // address-of // operator (non-const)
const Empty *pe1 = &e1; // address-of // operator (const)
Given that compilers are writing functions for you, what do the functions do? Well, the default constructor and the destructor don't really do anything. They just enable you to create and destroy objects of the class. (They also provide a convenient place for implementers to place code whose execution takes care of "behind the scenes" behavior see Items 33 and M24.) Note that the generated destructor is nonvirtual (see Item 14) unless it's for a class inheriting from a base class that itself declares a virtual destructor. The default address-of operators just return the address of the object. These functions are effectively defined like
inline Empty::Empty() {}
inline Empty::~Empty() {}
inline Empty * Empty::operator&() { return this; }
inline const Empty * Empty::operator&() const { return this; }
As for the copy constructor and the assignment operator, the official rule is this: the default copy constructor (assignment operator) performs memberwise copy construction (assignment) of the nonstatic data members of the class. That is, if m
is a nonstatic data member of type T
in a class C
and C
declares no copy constructor (assignment operator), m
will be copy constructed (assigned) using the copy constructor (assignment operator) defined for T
, if there is one. If there isn't, this rule will be recursively applied to m
's data members until a copy constructor (assignment operator) or built-in type (e.g., int
, double
, pointer, etc.) is found. By default, objects of built-in types are copy constructed (assigned) using bitwise copy from the source object to the destination object. For classes that inherit from other classes, this rule is applied to each level of the inheritance hierarchy, so user-defined copy constructors and assignment operators are called at whatever level they are
But just in case it's not, here's an example. Consider the definition of a NamedObject
template, whose instances are classes allowing you to associate names with
template<class T> class NamedObject { public: NamedObject(const char *name, const T& value); NamedObject(const string& name, const T& value);
...
private: string nameValue; T objectValue; };
Because the NamedObject
classes declare at least one constructor, compilers won't generate default constructors, but because the classes fail to declare copy constructors or assignment operators, compilers will generate those functions (if they are
Consider the following call to a copy constructor:
NamedObject<int> no1("Smallest Prime Number", 2);
NamedObject<int> no2(no1); // calls copy constructor
The copy constructor generated by your compilers must initialize no2.nameValue
and no2.objectValue
using no1.nameValue
and no1.objectValue
, respectively. The type of nameValue
is string
, and string
has a copy constructor (which you can verify by examining string
in the standard library see Item 49), so no2.nameValue
will be initialized by calling the string
copy constructor with no1.nameValue
as its argument. On the other hand, the type of NamedObject<int>::objectValue
is int
(because T
is int
for this template instantiation), and no copy constructor is defined for int
s, so no2.objectValue
will be initialized by copying the bits over from no1.objectValue
.
The compiler-generated assignment operator for NamedObject<int>
would behave the same way, but in general, compiler-generated assignment operators behave as I've described only when the resulting code is both legal and has a reasonable chance of making sense. If either of these tests fails, compilers will refuse to generate an operator=
for your class, and you'll receive some lovely diagnostic during
For example, suppose NamedObject
were defined like this, where nameValue
is a reference to a string and objectValue
is a const
T
:
template<class T> class NamedObject { public: // this ctor no longer takes a const name, because name- // Value is now a reference-to-non-const string. The char* // ctor is gone, because we must have a string to refer to NamedObject(string& name, const T& value);
... // as above, assume no // operator= is declared private: string& nameValue; // this is now a reference const T objectValue; // this is now const };
Now consider what should happen
string newDog("Persephone"); string oldDog("Satch");
NamedObject<int> p(newDog, 2); // as I write this, our dog //° Persephone is about to // have her second birthday
NamedObject<int> s(oldDog, 29); // the family dog Satch // (from my childhood) // would be 29 if she were // still alive
p = s; // what should happen to // the data members in p?
Before the assignment, p.nameValue
refers to some string
object and s.nameValue
also refers to a string
, though not the same one. How should the assignment affect p.nameValue
? After the assignment, should p.nameValue
refer to the string
referred to by s.nameValue
, i.e., should the reference itself be modified? If so, that breaks new ground, because C++ doesn't provide a way to make a reference refer to a different object (see Item M1). Alternatively, should the string
object to which p.nameValue
refers be modified, thus affecting other objects that hold pointers or references to that string
, i.e., objects not directly involved in the assignment? Is that what the compiler-generated assignment operator should
Faced with such a conundrum, C++ refuses to compile the code. If you want to support assignment in a class containing a reference member, you must define the assignment operator yourself. Compilers behave similarly for classes containing const
members (such as objectValue
in the modified class above); it's not legal to modify const
members, so compilers are unsure how to treat them during an implicitly generated assignment function. Finally, compilers refuse to generate assignment operators for derived classes that inherit from base classes declaring the standard assignment operator private
. After all, compiler-generated assignment operators for derived classes are supposed to handle base class parts, too (see Items 16 and M33), but in doing so, they certainly shouldn't invoke member functions the derived class has no right to
All this talk of compiler-generated functions gives rise to the question, what do you do if you want to disallow use of those functions? That is, what if you deliberately don't declare, for example, an operator=
because you never ever want to allow assignment of objects in your class? The solution to that little teaser is the subject of Item 27. For a discussion of the often-overlooked interactions between pointer members and compiler-generated copy constructors and assignment operators, check out Item 11.