Special Member Functions: Constructors, Destructors, and the Assignment Operator

http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=14

Objects are the fundamental unit of abstraction in object-oriented programming. In a broad sense, an object is a region of memory storage. Class objects have properties that are determined when the object is created. Conceptually, every class object has four special member functions: default constructor, copy constructor, assignment operator, and destructor. If these members are not explicitly declared by the programmer, the implementation implicitly declares them. This section surveys the semantics of the special member functions and their roles in class design and implementation.

 

Constructors

Last updated Mar 1, 2004.

A constructor initializes an object. A default constructor is one that can be invoked without any arguments. If there is no user-declared constructor for a class, and if the class doesn’t contain const or reference data members, C++ implicitly declares a default constructor for it. Such an implicitly declared default constructor performs the initialization operations needed to create an object of this type. Note, however, that these operations don’t involve initialization of user-declared data members. For example:

class C
{
private:
 int n;
 char *p;
public:
 virtual ~C() {}
};

void f()
{
 C obj; // 1 implicitly-defined constructor is invoked
}

C++ synthesized a constructor for class C because it contains a virtual member function. Upon construction, C++ initializes a hidden data member called the virtual pointer, which every polymorphic class has. This pointer holds the address of a dispatch table that contains all the virtual member functions’ addresses for that class. The synthesized constructor doesn’t initialize the data members n and p, nor does it allocate memory for the data pointed to by the latter. These data members have an indeterminate value once obj has been constructed. This is because the synthesized default constructor performs only the initialization operations that are required by the implementation—not the programmer—to construct an object.

Other implementation-required operations that are performed by implicitly-defined constructors are the invocation of a base class constructor and the invocation of embedded object’s constructor. However, C++ doesn’t declare a constructor for a class if the programmer has defined one. For example:

class C
{
private:
 int n;
 char *p;
public:
 C() : n(0), p(0) {}
 virtual ~C() {}
};
C obj; // 1 user-defined constructor is invoked

Now the data members of the object obj are initialized because the user-defined constructor was invoked to create it. This still leaves us with a mystery: The user-defined constructor only initializes the data members n and p. When did the initialization of the virtual pointer take place? Here’s the answer: The compiler augments the user-defined constructor with additional code, which is inserted into the constructor’s body before any user-written code, and performs the necessary initialization of the virtual pointer.

Trivial Constructors

As noted previously, compilers synthesize a default constructor for every class or struct, unless a constructor was already defined by the user. However, in certain conditions, such a synthesized constructor is redundant:

class Empty {};
struct Person
{
 char name[20];
 double salary;
 int age;
};
int main()
{
 Empty e;
 Person p;
 p.age = 30; // public access allowed, no constructor needed
 return 0;
}

C++ can instantiate Empty and Person objects without a constructor. In such cases, the implicitly declared constructor is said to be trivial. The fact that a constructor is considered trivial means that neither the programmer nor the compiler generates code for it. We’ll discuss this in greater detail shortly.

Constructors of Built-in Types

You might be surprised to hear this: Built-in types such as char, int, and float also have constructors. You can initialize a variable by explicitly invoking its default constructor:

char c = char();
int n = int ();
return 0;

This expression:

char c = char();

is equivalent to this one:

char c = char(0);

Of course, it’s possible to initialize a fundamental type with values other than 0:

float f = float (0.333);
char c = char ('a');
int *pi= new int (10);
float *pf = new float (0.333);

Note that this form is just “syntactic sugar.” It’s interchangeable with the more widely used form:

char c = 'a';
float f = 0.333;

Explicit Constructors

Constructors have many peculiar characteristics. They don’t have a name (and therefore can’t be called directly, as opposed to ordinary member functions); they don’t have a return value; and their address cannot be taken. Non-default constructors are even more odd. A constructor that takes a single argument operates as an implicit conversion operator by default. Today, I will explore this phenomenon in further detail and explain how to use the explicit qualifier to restrain constructors’ behavior.

Constructor Categories

In essence, constructors are what differentiates between a POD struct and a real object, as they automatically shape a raw chunk of memory into an object with a determinate state. A class may have several constructors (in this discussion, I’m referring to “plain constructors,” not to copy constructors), each of which taking a different number of arguments. A constructor that may be called without any arguments is a default-constructor.

A non-default constructor is one that takes one or more arguments. (The C++ literature provides no special name for this type of constructor, so I’m referring to it as a “non-default constructor.”) Non-default constructors are further divided into two subcategories: those that take a single argument and thus operate as implicit conversion operators, and those that take multiple arguments. For example,

class Date
{
public:
 Date(); // default ctor; no arguments required 
//non-default ctors:
Date(time_t t); // extracts date from a time_t value
Date(int d, int m, int y); //
};

The class Date has three constructors. The first one is a default constructor. The second one, which takes a single argument, operates as an implicit time_t to D conversion operator. As such, it will be invoked automatically in a context that requires a long to Date conversion (Remember that time_t is a synonym for long or a similar integral type). For example,

Date d=std::time(0);// invokes ctor # 2 to create d

You’re probably wondering how it works. Under the hood, the compiler transforms this code into something like this:

//pseudo C++ code 
time_t __value; 
__value = std::time(0);
Date __temp(__value);//create a temporary Date object
Date d = __temp; 
temp.C::~C(); //destroy temp

In this example, the automatic conversion is intentional and useful. Yet, there are many cases in which an implicit conversion is undesirable, for example:

Date p=NULL; //actually meant 'Date *p=NULL'; compiles OK

Can you see what’s happening here? NULL is defines as 0 or 0L in C++. The compiler silently transformed the code above into:

//pseudo C++ code 
Date temp(0L);//create a temporary Date object
Date d = temp; //assigned using operator =
temp.C::~C(); //destroy temp

The explicit Keyword

The problem here is that the implicit conversion (taking place behind the programmer’s back) switches off the compiler’s static type checking. Without this implicit conversion, the compiler would have complained about a type mismatch. C++ creators perceived this problem long ago. They decided to add a “patch” to the language in the form of the keyword explicit. Constructors declared explicit will refuse to perform such implicit conversions:

class Date
{
//...
 explicit Date(time_t t); // no implicit conversions
};

Now, the previous examples will not compile:

Date d =std::time(0); //error, can't convert 'long' to 'Date' 
Date p=NULL; //also an error

To convert a time_t or any other integral value to a Date object, you need to use an explicit conversion now:

Date d(std::time(0)); //OK
Date d2= Date(std::time(0)); //same meaning as above

Advice

If you examine a code corpus, say the Standard Library, you will see that most of the constructors taking one argument are explicit. One therefore could argue that this should have been the default behavior of constructors, whereas constructors that permit implicit conversions should have been the exception. Put differently, instead of explicit, C++ creators should have introduced the keyword implicit and changed the semantics of constructors so that they didn’t function as implicit conversion operators.

However, this approach would have caused existing code to break. In some classes, for example std::complex and other mathematical classes, implicit conversions are rather useful. The C++ creators therefore decided to leave the original semantics constructors intact, while introducing a mechanism for disabling implicit conversions, when necessary.

As a rule, every constructor that takes a single argument, including constructors that take multiple arguments with default values such as the following, should be explicit, unless you have a good reason to allow implicit conversions:

class File
{
public:
 //this ctor may be called with a single argument
 //it's therefore declared explicit:
 explicit File(const char *name, 
        ios_base::openmode mode=ios_base::out,
        long protection = 0666);
};

Member Initialization Lists

A constructor may include a member initialization (mem-initialization for short) list that initializes the object’s data members. For example:

class Cellphone //1: initialization by mem-init
{
private:
 long number;
 bool on;
public:
 Cellphone (long n, bool ison) : number(n), on(ison) {}
};

The constructor of Cellphone can also be written as follows:

Cellphone (long n, bool ison) //2: initialization within ctor's body
{
 number = n;
 on = ison;
}

There is no substantial difference between the two forms in this case because the compiler scans the mem-initialization list and inserts its code into the constructor’s body before any user-written code. Thus, the constructor in the first example is expanded by the compiler into the constructor in the second example. Nonetheless, the choice between using a mem-initialization list and initialization inside the constructor’s body is significant in the following four cases:

  • Initialization of const members. For example:
    class Allocator
    {
    private:
     const int chunk_size;
    public:
     Allocator(int size) : chunk_size(size) {}
    };
  • Initialization of reference members. For example:
    class Phone;
    class Modem
    {
    private:
     Phone & line;
    public:
      Modem(Phone & ln) : line(ln) {}
    };
  • Passing arguments to a constructor of a base class or an embedded object. For example:
    class base
    {
    //...
    public:
      //no default ctor
     base(int n1, char * t) {num1 = n1; text = t; }
    };
    class derived : public base
    {
    private:
     char *buf;
    public:
     //pass arguments to base constructor
     derived (int n, char * t) : base(n, t) { buf = (new char[100]);}
    };
  • Initialization of member objects. For example:
    #include<string>
    using std::string;
    class Website
    {
    private:
     string URL
     unsigned int IP
    public:
     Website()
     {
      URL = "";
      IP = 0;
     }
    };

In the first three cases, a mem-initialization list is mandatory because these initializations must be completed before the constructor’s execution. Conceptually, initializations listed in a member-initialization list take place before the constructor executes. In the fourth case, the use of a mem-init list is optional. However, it can improve performance in certain cases because it eliminates unnecessary creation and subsequent destruction of objects that would occur if the initialization were performed inside the constrictor’s body.

Due to the performance difference between the two forms of initializing embedded objects, some programmers use mem-initialization exclusively. Note, however, that the order of the initialization list has to match the order of declarations within the class. This is because the compiler transforms the list so that it coincides with the order of the declaration of the class members, regardless of the order specified by the programmer. For example:

class Website
{
private:
 string URL; //1
 unsigned int IP; //2
public:
 Website() : IP(0), URL("") {} // initialized in reverse order
};

In the mem-initialization list, the programmer first initializes the member IP and then URL, even though IP is declared after URL. The compiler transforms the initialization list to the order of the member declarations within the class. In this case, the reverse order is harmless. When there are dependencies in the order of initialization list, however, this transformation can cause surprises. For example:

class Mystring
{
private:
 char *buff;
 int capacity;
public:
 explicit Mystring(int size) :
 capacity(size), buff (new char [capacity]) {} undefined behavior
};

The mem-initialization list in the constructor of Mystring doesn’t follow the order of declaration of Mystring‘s members. Consequently, the compiler transforms the list like this:

explicit Mystring(int size) :
buff (new char [capacity]), capacity(size) {}

The member capacity specifies the number of bytes that new has to allocate, but it hasn’t been initialized. The results in this case are undefined. There are two ways to avert this pitfall: Change the order of member declarations so that capacity is declared before buff, or move the initialization of buff into the constructor’s body.

Copy Constructor

A copy constructor initializes its object with another object. If there is no user-defined copy constructor for a class, C++ implicitly declares one. A copy constructor is said to be trivial if it’s implicitly declared, if its class has no virtual member functions and no virtual base classes, and if its entire direct base classes and embedded objects have trivial copy constructors. The implicitly defined copy constructor performs a memberwise copy of its sub-objects, as in the following example:

#include<string>
using std::string;
class Website //no user-defined copy constructor
{
private:
 string URL;
 unsigned int IP;
public:
 Website() : IP(0), URL("") {}
};
int main ()
{
 Website site1;
 Website site2(site1); //invoke implicitly defined copy constructor
}

The programmer didn’t declare a copy constructor for class Website. Because Website has an embedded object of type std::string, which happens to have a user-defined copy constructor, the implementation implicitly defines a copy constructor for class Website and uses it to copy-construct the object site2 from site1. The synthesized copy constructor first invokes the copy constructor of std::string, and then performs a bitwise copying of the data members of site1 into site2.

Novices are sometimes encouraged to define the four special member functions for every class they write. As can be seen in the case of the Website class, not only is this unnecessary, but it’s even undesirable under some conditions. The synthesized copy constructor (and the assignment operator, described in the next section) already “do the right thing.” They automatically invoke the constructors of base and member sub-objects, they initialize the virtual pointer (if one exists), and they perform a bitwise copying of fundamental types. In many cases, this is exactly the programmer’s intention anyway. Furthermore, the synthesized constructor and copy constructor enable the implementation to create code that’s more efficient than user-written code, because it can apply optimizations that aren’t always possible otherwise.

 

Constructors FAQ, Part I

Last updated Sep 21, 2007.

A detailed account of standard C++ rules regarding constructors and the rest of the so-called special member functions is available here since the early days of the C++ Reference Guide. However, the number of questions about constructors on various C++ forums and emails that I receive recently suggests that this topic is still very confusing and surrounded by myths. Therefore, I believe that a supplementary column in the form of frequently-asked questions will dispel those myths and misunderstandings.

Question: What is a default constructor?

Answer: Many programmers believe that a default constructor is a constructor that the compiler automatically generates when the user doesn’t define a constructor. This isn’t correct. A default constructor can be user-defined or compiler-generated; it’s a default constructor if and only if it can be invoked without any arguments. The following classes all have default constructors:

struct A
{
int x;
A(): x(0) {}
};
struct B: A
{
//no user-defined ctor.
//the compiler implicitly declares a default constructor
};
class C
{
public:
explicit C(int n=0, int m=0); // may be invoked with no arguments
};
C c0, c2(9,0);

As class C demonstrates, the default constructor needn’t be declared with an empty parameter list; C’s constructor qualifies as a default constructor because it may be invoked with no arguments.

Question: When does the compiler implicitly declare a default constructor?

Answer: The compiler will implicitly declare a default constructor for any class that doesn’t declare any constructor explicitly, provided that the class in question doesn’t contain any reference or const data members. Notice that if the user declares a copy constructor, that constructor will preclude the compiler’s implicit declaration of a default constructor. Consider:

class A
{
 int x;
public:
 void f();
};//no user declared constructors of any kind; 
A a; //OK, using implicitly-declared default ctor

The compiler implicitly declares a default constructor, copy constructor (as well as an assignment operator and a destructor which I will not discuss here) for class A because the implementer of this class didn’t declare any constructor for it. Let’s examine this issue more closely. Suppose the user adds the following copy constructor declaration:

class A2
{
 int x;
public:
 A2(const A2&); //the access type doesn’t matter, it could be private or protected too
void f();
};

The compiler doesn’t declare an implicit default constructor for class A2 because the user has already declared a constructor. Even if the user-declared constructor is a copy constructor or a constructor taking multiple arguments, that’s enough to preclude the implicit declaration of a default constructor! This means that A2 doesn’t have a default constructor at all:

A2 a2; //error, A2 has no default constructor

As an aside, you can’t even use the copy-constructor to instantiate an object of type A2 because you can’t instantiate the argument for the copy constructor:

A2 a3(a2); //a2 cannot be instantiated because no default ctor is available

Question: What does the implicitly declared default constructor do?

Answer: Here’s another common myth — many believe that the implicitly-declared default constructor does secret initialization operations behind their back, or that it allocates memory for the object. This is sheer nonsense. The implicitly-declared constructor does absolutely nothing! Remember: declaring a constructor — either explicitly or implicitly — doesn’t necessarily mean that the constructor is also defined. The following examples demonstrate the difference between a constructor declaration and its definition:

class B
{
 B(); //declaration only
};
B::B() {} //separate definition of the user-declared default constructor

class C
{
C() {} //declaration and definition
};
class D
{
//implicit declaration of a default constructor, no implicit definition
};
class E
{
//implicit declaration + implicit definition of a default constructor
virtual void f();
};
class F
{
virtual void f();
public:
F(); ////explicit declaration a default constructor, user forgot a definition
};

In the last example, the lack of a definition for F’s default constructor is most likely a programmer’s omission. This type of a human error will cause a linkage error when you try to instantiate this class:

F f; //linkage error: symbol ’F::F()’ could not be resolved

To conclude, a user-declared constructor is never implicitly-defined. If the user has declared a constructor, he or she is responsible for defining it as well, unless they intentionally left it unimplemented (to prevent object copying for instance). By contrast, an implicitly-declared constructor may be implicitly-defined under certain conditions which I will explain in the second part of this series.

The second part of this series will also answer additional questions including: When does the compiler implicitly define a default constructor? Is it true that the implicitly-defined constructor allocate memory for its object? Does the implicitly-defined constructor initialize the object’s data members?

 

 

Constructors FAQ, Part II

Last updated Sep 28, 2007.

The second installment lists the conditions under which an implicitly-declared constructor will also be implicitly-defined. It also explains why the compiler implicitly declares constructors even when they are not defined, seemingly for no purpose.

Question: When Does the Compiler Implicitly-Define An Implicitly-Declared Constructor?

Answer: An implicitly-declared default constructor is implicitly-defined if and only if the implementation needs that definition. The implementation needs an implicit definition in the following cases:

Classes with virtual member functions. A class that has virtual member functions must have a non-trivial constructor. A non-trivial constructor is either a user-defined constructor or a constructor that is implicitly defined. The non-trivial constructor is responsible for internal initializations such as assigning the correct vptr address of each polymorphic object.

Base and embedded subobjects. A class must have a non-trivial constructor if it has base class(es) with non-trivial constructor(s) or embedded objects with non-trivial constructor(s). The non-trivial constructor has to initialize the base class(es) and the embedded object(s) first. For example:

struct T
{
T() {cout<<"hello"<<endl;} //user-defined constructor
};
struct U:T
{
//default ctor implicitly defined 
};

The compiler implicitly-defines a default constructor for U. U’s constructor invokes the non-trivial constructor of the base class T whenever you instantiate an object of type U:

U u; //implicitly defined ctor invokes T::T() first.

Virtual inheritance. The presence of a virtual base class necessitates a non-trivial constructor to ensure that only one virtual subobject is present in every derived object. Therefore, if a class has any virtual base class(es), its constructor must be non-trivial.

Question: Why Does The Compiler Implicitly Declare Trivial Constructors If They Are Never Defined?

Answer: Under certain conditions, the compiler implicitly-declares a default constructor but doesn’t define it. Such a constructor is said to be trivial. Many C++ programmers find this notion of trivial member functions rather confusing. Why on earth does the compiler bother to declare a member function implicitly when it knows that that function won’t be implicitly defined? Do implicit declarations of this kind serve any purpose?

The first thing to bear in mind is that implicit declarations (and implicit definitions) are conceptual. That is, the compiler doesn’t go through your header files and inserts lines of code with constructor declarations to them. Rather, the compiler, the linker and the program behave as if the trivial member functions were declared. In practice, the compiler simply sets a few bits in a class’ type information record, indicating that the said type has a (trivial) default constructor declaration. So if you’re worried about excess verbiage of source files or the potential bloat of executable files, rest assured that nothing of this kind should happen.

Now let’s get to the more important conundrum: why bother at all with these trivial declarations? After all, C doesn’t need this mechanism for its structs and unions. The truth is that implicit declarations have a contractual role. Each implicit declaration is like an article in a contract that states how a certain class may be used. When the compiler implicitly declares one of the special member functions, it grants certain authorizations to the user. By contrast, if the compiler doesn’t implicitly-declare a member function, the user is restricted with respect the class’s usage. Consider:

struct Blocked
{
public:
Blocked(const Blocked&);
};

By now you know that an explicit declaration of a copy-constructor precludes the implicit declaration of a default constructor. Since the default constructor is neither implicitly nor explicitly declared, you can’t instantiate objects of class Blocked:

Blokced b; //error, no default constructor available
Blocked *p = new Blocked; //error

Without the implicit declarations mechanism, programmers would have to go through every class and struct, manually adding declarations of every member function that’s needed for instantiating, copy-constructing, assigning and destroying objects. The following POD type demonstrates this:

struct Date
{
int day, month, year;
};

The compiler implicitly declares the following member functions for Date:

  • A trivial default constructor
  • A trivial copy constructor
  • A trivial assignment operator
  • A trivial destructor

These implicit declarations enable you to use Date as follows:

Date d; //implicit declaration of default ctor and dtor allow this
Date * pdate= new Date; //same here
Date d2(d); //implicit copy ctor declaration allows this
*pdate=d2; //implicit assignment operator declaration allows this
delete pdate; //implicit dtor declaration allows this

The trivial member functions play an important role in C++ programming. To realize how much time and drudgery they save, try to circumvent their implicit declaration by adding explicit declarations to the class:

struct Date
{
int day, month, year;
private:
 ~Date(); //declared but not defined
 Date(const Date&); //ditto
};

Now the following code no longer compiles:

Date d; //error, no default ctor
Date * pdate= new Date; //same error here
Date d2(d); //error, no accessible copy ctor

Coming Next

The next installment will answer the following questions and explain the rationale behind the answers: does an implicitly-defined constructor allocate memory for its object? Does an implicitly-defined constructor initialize user-declared data members?

 

 

Constructors FAQ, Part III

Last updated Oct 5, 2007.

The last installment of this series is dedicated to dispelling two common myths about constructors in general: the memory allocation myth and the data member initialization myth.

Question: Does An Implicitly-Defined Constructor Allocate Memory For Its Object?

Answer: No, it doesn’t. The widespread myth about implicitly-defined constructors allegedly allocating memory for their object is as old as C++. However, it’s completely groundless. It doesn’t matter which storage type you choose for your object — by the time the constructor starts executing, the raw memory for the object has already been allocated. Therefore, the constructor doesn’t need to allocate any additional memory for the user-declared data members of the object. The same generalization applies to user-defined constructors — they never allocate memory behind your back either.

To understand this issue better, let’s look at three objects of the same class, each using a different storage type:

std::string s1; //static storage duration
int main()
{
std::string s2; //auto storage 
std::string * p = new string; //free store
}

Consider s1. The implementation allocates a chunk of memory on the static storage region conceptually before main() starts. The constructor of s1 is invoked only after this allocation has taken place. The object s1 has a user-defined constructor (meaning the library implementer wrote the constructor, not the compiler). However, even if you used a class with an implicitly-defined constructor, that constructor would be called only after the implementation has allocated the raw memory for its object:

struct S
{
 virtual void f(){} //has an implicitly-defined constructor
};
S s; 
int main()
{}

s2 is created in a similar fashion. First the implementation allocates storage on the stack for s2. The word “allocates” is a bit misleading in this context. Allocating memory on the stack consists of two simple operations: storing the current stack pointer in a register or another temporary variable, and decreasing the current stack pointer (remember that the stack grows downwards) by sizeof(s2) bytes. Here again, C++ uses the same approach as C, so there’s no call for any magical constructors at this stage. The constructor of s2 executes only after the stack pointer has been decreased (a push operation). The constructor initializes the raw stack memory chunk, shaping it into a valid string object. Not a single byte of additional stack memory is allocated during the construction stage.

User-defined constructors often have new expressions; in that case, the constructor does allocate memory but there are two important points you should bear in mind:

  • Memory allocated by a new expression in the constructor is not part of the object’s space. It doesn’t matter how much memory your new expression allocates, sizeof (*this) remains fixed.
  • Implicitly-defined constructors never call any allocations functions such as new, malloc(), calloc() etc.

Let’s examine the third object. When the statement

p=new string;

is executed, new first invokes an allocation function that allocated raw memory from the free-store. That allocation function is not related in any way to the constructor; it’s the equivalent of C’s malloc().

After the allocation function has returned successfully the constructor is invoked, initializing the allocated free-store memory and turning it into a string object.

To conclude, there never is a stage in which the constructor itself — either user-defined or implicitly-defined — allocates memory behind your back. C++ programmers are taught to view a constructor invocation as the first operation that takes place when they instantiate an object. However, under the hood there are a few other low-level operations that precede the constructor. These low-level operations are not part of the constructor.

Question: Will the implicitly-defined constructor zero-initialize my data members?

In other programming languages, constructors do more than they are told to. Not in C++ though. If you didn’t define a constructor with explicit initializers for the class’s POD data members, those members will have an indeterminate value. This rule applies to user-defined constructors and implicitly-defined constructors alike — neither will initialize user-declared POD data members on its own accord:

struct A 
{
int x;
virtual ~A(); //class has an implicitly-defined ctor
};
struct B
{
int x;
B() {} //user-defined ctor with no explicit initializer for x
};
int main()
{
 A a; //a.x has a garbage value 
 B b; //b.x has a garbage value 
}

In the absence of explicit initializers, a non-trivial constructor will only initialize data members that must be initialized:

  • a vptr (if the class or any of its base classes have virtual member functions )
  • base class and embedded subobjects (if they have nontrivial constructors)
  • address(es) of virtual base classes (regardless of whether the virtual base has a nontrivial constructor).

The last item is slightly confusing. The constructor doesn’t initialize data members of a virtual base class subobject. It initializes a local pointer (which you can’t access directly) making it point to the correct address of a shared virtual base class.

Bearing in mind that implicitly-defined constructors never initialize user-declared data members, the data members of A and B are still zero-initialized in some cases:

namespace NS
{
 A a; //namespace scope objects have static storage type
}
void f()
{
static B b; //b.x is zero initialized like as all static objects
}

In these two cases, the zero initialization of x takes place before the constructor is invoked. This process is called static initialization. Static initialization always precedes any constructor call. Here again, C is a good reference point. Global and local static data in C are zero-initialized by default. The zero initialization process relies on the operating system’s memory management and has nothing to do with constructors. Even if a.x and b.x appear to have been initialized, that initialization took place independently of any constructor.

One could argue that leaving data members with an indeterminate value is a bad idea (especially when teaching C++ to an ex-Java programmer). However, the policy of “do not initialize unless explicitly asked to” has two compelling reasons:

Performance. Initialization of data members exacts a performance toll. If programmers don’t need to have their members initialized, there’s no reason why they should pay for it.

Compatibility with C. In C instances of structs and unions aren’t initialized by default (unless they have static storage type). The designers of C++ wanted to preserve the same behavior in C++ to make programs’ behavior identical in both languages and because there are times when programmers really don’t want to have data members initialized automatically.

Therefore, if you want your POD data members initialized, provide explicit initializers for them. The constructor will not do that for you.

 

 

C++0x Constructor-Related Features FAQ, Part I

Last updated Sep 25, 2009.

The C++0x standard has added many new features that are related to constructors, including delegating constructors, inheriting constructors, class member initializers, new initialization syntax, initializer lists and sequence constructors, and move constructors. This proliferation can be confusing. Therefore, this FAQ series will clarify these features by answering frequent questions related to those features. The first part discusses delegating constructors and inheriting constructors.

Question: Isn’t There an Overlap Between Some Of The C++0x Constructor-Related Features?

Answer: Not really. Indeed, many of the said features are related to initialization but remember that constructors are essentially object initializers. The C++0x constructor-related features listed above aren’t interchangeable since they are applicable to different contexts and use cases. Take for example delegating constructors and inheriting constructors. They might look similar syntactically but delegating are constructors that invoke other constructors of the same class. Inheriting constructors pertain solely to a class that is derived from one or more immediate base classes.

Question: What Are Delegating Constructors?

Answer: When a class has multiple user-declared constructors, it make sense to perform common initializations in only one of those constructors (known as the target constructor), letting other constructors (the delegating constructors) call the target constructor. This way, you avoid repeating the same initialization steps in every constructor.

A delegating constructor looks like an ordinary constructors with a member initialization list. However, the member initialization list of a delegating constructor always includes a target constructor of the same class:

struct C 
{
 C(): x(0), y(0) {} // target ctor, will be called by other ctors of class C
 C(int n) : C() {/*..*/} // delegating ctor invokes target ctor
 C(const char * p) : C() {/*..*/} // delegating ctor
private:
 int x, y;
}

Question: Can A Delegating Constructor Be A Target Constructor As Well?

Answer: Yes, it can. You can chain constructor calls in the following manner:

struct S
{
 S() {cout<<"first target"<<endl;} #1
 S(const char *p): S() {cout<<p<<endl;} #2
 S(int n): S("second target")   {cout<<"finally, delegating ctor"<<endl;}#3
};
S s(1);

Constructor #1 is the target constructor of the delegating constructor # 2. Constructor #2 is the target constructor of constructor # 3. As you can see, there are no special syntactic cues that designate a target constructor. Similarly, a delegating constructor doesn’t have a special syntactic marker. The compiler and the human reader can tell a delegating constructor from an ordinary constructor with a simple test: does the initializer list include a constructor of the same class or not? Consider:

struct D: A
{
 D() : A() {} //ordinary ctor, not a delegating ctor
};

D’s constructor isn’t a delegating constructor because its initialization list invokes the constructor of a different class, A (which happens to be the base class of D). By contrast, the following class has a constructor T(int) whose member initializer include another constructor — a target constructor –of the same class:

struct T
{
 T(){} //target ctor
 T(int n) : T() {} //call target constructor
};

Question: Let’s Talk About Inheriting Constructors. What Are They Exactly?

Answer: Inheriting constructors are compiler-generated constructors declared in a derived class. Their signatures are identical to those of the constructors declared in the base class. The compiler declares inheriting constructors in a derived class if the programmer has explicitly requested that by including a using-declaration that names the base class constructor. Here’s an example:

struct A
{
 A(int);
 A(double);
};

struct B: A
{
 using A::A; //an inheriting ctors request
};

The using declaration in B instructs the compiler to implicitly-declare two constructors having the same signatures as the two constructors in A, namely B(int) and B(double). These implicitly-declared constructors are called inheriting constructors because they inherit the signature, access, constexpr, =delete and similar properties of the base class constructors. Without the using declaration, the programmer would have had to write the forwarding constructors of B manually like this:

struct B: A
{
 B(int n) : A(n) {}
 B(double d) : A(d) {}
};

Not only is this tedious, but it also might cause maintenance problems because every change made to the base class constructors requires updating the derived classes’ constructors accordingly.

Question: What Are Inheriting Constructors Good For? What Do They Do?

Answer: Inheriting constructors eliminate the tedium of defining derived class constructors that merely forward their argument(s) to a base class constructor with the same signature. The implicitly-declared inheriting constructors are implicitly-defined only if they are actually used in the program. The implicitly-defined inheriting constructors forward their arguments to a base class constructor with the same signature using rvalue references but they do not initialize user-declared data members of the derived class. Notice that the copy constructor and the default constructor aren’t forwarded, which means that you need to declare them explicitly in a derived class when necessary.

Question: What If I Want Only Some Inheriting Constructors But Not All Of Them?

Answer: It’s an all-or-nothing thing. There is no way to instruct the compiler to exclude one or more of the constructors from the list of inheriting constructors. However, you can explicitly declare constructors in the derived class. If a user-declared and an implicitly-declared constructor have the same signature, the user-defined one takes precedence, effectively hiding the implicitly-declared inheriting constructor:

struct A
{
 A(int);
 A(double);
};
struct B
{
 B(double);
};

struct D: A, B
{
 using A::A; //implicitly-declare B(int) and B(double)
 using B::B; //implicitly-declare B(double), conflicts with the above
 D(double); //override the implicitly-declared D(double)
};

B b(5.9); //OK, calling the user-declared ctor



C++0x Constructor-Related Features FAQ, Part II

Last updated Oct 2, 2009.

The second part of this series focuses on move constructors and related features: move operations, and rvalue references.

Question: What Are Move Constructors and How do They Differ From Copy Constructors?

Answer: Move constructors are similar to copy constructors in the sense that they both initialize a new object to the state of an existing object. However, there’s a crucial difference between the two: while copy construction doesn’t modify the source object, a move constructor doesn’t guarantee that the source object will retain its state after the move operation. This cautious wording usually means that a move constructor often pilfers the source object from its resources, transferring them to the target object.

Take for example a string object that is the source object. When you copy construct another string object from the source, you end up having two string objects with the same state — the source object (which has retained its state) and the newly created object. By contrast, moving strings empties the source string from its resources so the end result is a string object that has the state that the source string formerly had. Consider:

string s1="test";
string s2(s1); //copy operation, s1 is an lvalue
cout<<s1<<endl; //output: test
cout<<s2<<endl; //output: test
string s3=std::move(s1);
cout<<s1<<endl; //output:    (empty string)
cout<<s3<<endl; //output: test

Syntactically, a move constructor differs from a copy constructor. A move constructor takes X&& as its parameter (where X is the name of the class) as opposed to a copy constructor that takes X& (with possible const and volatile qualifications):

class C
{
//...
public:
 C(const C&); //copy constructor
 C(C&&); //move constructor
};

The parameter X&& is an rvalue reference to X.

Question: What Exactly Are Rvalue References?

Rvalue references are a new core language feature introduce in C++0x. Unlike ordinary references, which bind to lvalues, rvalue references bind exclusively to rvalues (yes, they no longer bind to lvalues). Let’s look closer at rvalue references.

In C++03 you can’t bind ordinary references to rvalues. Consider:

void f(int &r);
f(3); //error, 3 is an rvalue

To overcome this limitation, you have to declare a reference to const instead:

void f(const int &r);
f(3); //OK, 3 is an rvalue but it can bind to a reference to const

However, in order to bind an rvalue to a reference to const, the compiler might still need to create a temporary object that will silently bind to the reference. Rvalue references make this code more efficient since the compiler doesn’t need to create temporaries when binding rvalues to rvalue references nor does the programmer need to const-qualify the reference:

void f(int &&r);
f(3); //OK, 3 is an rvalue that binds to an rvalue reference

Traditionally, lvalues are defined as objects that can appear on the left-hand side of an assignment expression. Similarly, rvalues are objects that may appear on the right-hand side of an assignment expression. Rvalues include literals (quoted strings, numerals), temporary objects, and array names. (You can find more information about the history of the terms rvalues and lvalues here). The addition of rvalue references to C++0x thus fills in a gap in the language’s type system, which has hitherto prevented you from binding rvalues to references safely.

Question: Are The Copy Constructor and Move Constructor Mutually Exclusive?

Answer: Technically, they aren’t mutually exclusive. You can define both of them in one class. In such cases, the compiler determines which of the two constructors will be called based on the new overload resolution rules. If the argument is an rvalue, it will bind to the rvalue references of the move constructor. Otherwise, the argument is an lvalue and the copy constructor will be called, as ever. In some cases however the designer of the class will define either the copy constructor or the move constructor.

Question: Why Are Move Constructors Needed?

Answer: In real life, you can distinguish intuitively between move operations and copy operations. Transferring money from one bank account to another is a move operation because the total amount of money in both accounts hasn’t changed as a result of the transfer. Similarly, relocating from one apartment to another is a move operation. Copying a photograph is a copy operation because you end up having two identical photographs (unless the copy process destroys the original, alas).

Move constructors are meant to simulate move operations that transfer resources from one location to another instead of multiplying the resources. They are used when you want to avoid the creation of short-lived temporary objects (e.g., swapping objects), in algorithms that are not allowed to create temporaries or when you must make sure that a container shall not reallocate. In general, moving is more efficient than copying because it doesn’t use new resources but merely redistributes the currently used resources. However, it requires familiarity with the intimate implementation details of the object in question.

Question: Does the Compiler Automatically Declare a Move Constructor For Every Class?

Answer: No, it doesn’t. Move constructors are in this respect different from the four canonical member functions which the compiler implicitly-declares by default. If a class needs a move constructor, you need to declare a move constructor explicitly.

Question: So Do I Need To Declare a Move Constructor in Every Class I Design?

Answer: No, you don’t. You need to declare a move constructor only in classes for which moving makes sense. Notice that a move operation isn’t as trivial as copying because transferring resources often requires awareness of the underlying implementation of the class in question. For example, moving file A to file B requires not just copying the data of A to B but also erasing the data from A. That doesn’t mean that moving files is less efficient than copying files — on the contrary! (in most cases, moving files merely consists of swapping symbolic links).

 

 

 

C++0x Constructor-Related Features FAQ, Part III

Last updated Oct 9, 2009.

The third part of this series focuses on new initialization forms, including class member initializers, new initialization syntax, initializer lists and sequence constructors.

Question: Can You Outline The New Initialization Rules of C++0x?

Answer: The new initializations rules cover various aspects of initializing containers, class objects, POD variables and aggregates. The new rules aim to solve two problems that have hitherto troubled C++ programmers:

  • Lack of a uniform initialization notation.
  • Inability to initialize certain variables.

With respect to uniformity, the new initialization notation uses the brace notation all across the board. Therefore, you can use the same notation to initialize POD types, arrays, containers and class objects — instead of using three or four different initialization forms. Consider:

int n={0}; //C++0x. Same as C++03 int n=0; and int n(0);
class XY 
{ public:   XY( int x, int y );   // ... }; 
XY point={1,0}; //C++0x. Same as C++03 XY point(1,0);

The new initialization rules allow you to initialize aggregates that you haven’t been able to initialize up until now. These include array members of a class, arrays of POD types allocated with new[], and containers. The following examples demonstrate this:

class C { //C++0x. initialize a member array
  int a[4];
public:
  C() : a{ 1, 2, 3, 999} {}
};
int* a = new int[4] { 1, 2, 3, 999}; //C++0x. initialize a dynamically allocated array
vector <int> vi={1,2,3.99}; //C++0x. initialize a container
map<string,string> MammaMiaContacts =
  { { "Benny Andesson (mobile)", "+44 (20) 5555-1212 },
    { "Bjorn Ulvaeus (hotel)", "+1 (212) 5555-1234 }};

Question: How Is The New Class Member Initializer Feature Related To All of That?

Answer: Class members initializers are a new feature that enables you to define a default initializer for a data member of a class. That default initializer is automatically translated to a member initializer in every constructor of that class, unless the programmer overrode the default by providing an explicit initializer in the constructor. A class member initializer is particularly useful in a class that defines multiple constructors, where several constructors need to initialize the data member in question to the same value. Let’s look at an example:

class Buffer
{
 char* buff=nullptr; //C++0x class member initializer
public:
 Buffer() {}
 Buffer(bool cached) {/**/}
 Buffer (const string & encryption_mode);
 Buffer(size_t intial_size) :buff (new char[intial_size]) {/**/}
};

Class Buffer has four user-declared constructors. The first three need to initialize buff to nullptr. The declaration of buff thus includes a class member initializer that these constructors pick. Effectively, a class member initializer is transformed into a member initializer in a constructor that doesn’t provide an explicit initializer for the said member. The compiler silently rewrites the first three constructors as follows:

Buffer() :buff(nullptr) {}
Buffer(bool cached): buff(nullptr) {/**/}
Buffer (const string & encryption_mode): buff(nullptr) {/**/}

The fourth constructor includes an explicit initializer for buff that overrides the class member initializer. Notice that a class member initializer may use any initialization notation suitable for the relevant data member. For example, you may initialize a string object like this:

class A
{
 std::string name={"Bruce"};
public:
 A(); //pick the class member initialize for name
 A(const std::string & nm) name(nm){}
};

Question: How Does The New Initialization Notation of Containers Work?

Answer: The secret lies in a new construct called a sequence constructor. The C++0x Standard Library containers were silently augmented with this new constructor so you can now use the new initialization notation if your compiler supports C++0x extensions and your Standard Library is C++0x-compatible.

Question: What Exactly Is A Sequence Constructor?

Answer: A sequence constructor is one that takes a parameter of type std::initializer_list<T>. std::initializer_list is a class template defined in the standard C++0x header <initializer_list>, which a C++ compiler automatically #includes in a translation unit that refers to std::initializer_list (yes, this is the first and only case in which a standard C++ compiler implicitly #includes a standard header).

The std::initializer_list<T> template converts a sequence of values of type T into an array of T. That array (referred to as the range) is then used to populate the container. Here’s an example of a class with a sequence constructor:

class C
{
 //initialize C with doubles
 C(std::initializer_list<double>); 
};
//usage
C c1={1.5,0.99,23.45,-1.87};

Notice that the initializer_list object is passed by value.

Question: Should I Add A Sequence Constructor To My Classes From Now On? Does A Sequence Constructor Incur Any Performance Overhead?

Answer: The answer to both questions is “No”. A sequence constructor is meaningful only in classes that support a variable number of initializers, e.g., containers. Only if you’re designing a class that supports a variable number of initializers, say a class that simulates an array, should you consider defining such a constructor. With respect to performance, an initializer_list is a template that forms an array at compile time. Therefore, the runtime overhead of an initializer-list is negligible.

Question: Can I Already Use The C++0x Constructor-Related New Features?

Answer: Certain implementations already support at least some of the features presented in this series. Other implementations will start supporting these features soon, too. In most cases, you need to enable C++0x support explicitly using a compiler-specific flag.

 

 

 

Assignment Operator

Last updated Mar 1, 2004.

An assignment operator assigns to its object another object. For example:

std::string s1="hello"
std::string s2;
s2=s1;

If there’s no user-defined assignment operator for a class, C++ implicitly declares one. An implicitly declared assignment operator has the following form:

C& C::operator=(const C&);

An assignment operator is said to be trivial if it’s 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.

 

 

Copy Constructor and Assignment Operator FAQ, Part I

Last updated Oct 19, 2007.

Following the Constructors FAQ series, the sequel dedicated to the copy constructor and the assignment operator was just a matter of time. The copy constructor FAQ explains the semantic differences between these two special member functions and as usual, it dispels some myths about them.

Question: Do I have to define a copy constructor and assignment operator?

Answer: C++ implicitly declares a copy constructor and an assignment operator for every class, struct and union unless the user declared them explicitly. A copy constructor isn’t implicitly declared if the class has any user-declared constructor(s). Implicitly defined copy constructor and assignment operator are said to be trivial if:

  • their class has no virtual functions and no virtual base class(es)
  • all direct base classes and nonstatic data members of their class have trivial constructors

Otherwise, the copy constructor and the assignment operator are non-trivial. Implicitly-declared non-trivial copy constructor and assignment operator are implicitly-defined.

The assignment operator is called “copy assignment operator” in the standard. This verbosity doesn’t convey any new or hidden meanings. Perhaps it’s meant to differentiate between the assignment operator of fundamental types and the assignment operator member function of class types. In this series I will stick to the term assignment operator.

Question: Is there a fundamental different between the copy constructor and the assignment operator?

Answer: Yes, there is. Although the copy constructor and the assignment operator have similar functions, they differ in one crucial aspect. A copy construction operation is a constructor call by all means. That is, the object being constructed during a copy constructor invocation has an indeterminate state until the copy constructor has returned successfully. Furthermore, a copy constructor doesn’t return any value (not even void) — very much like any other constructor whereas an assignment operator has a return value.

A copy constructor will be called only when a new object is being instantiated. The assignment operator is called for an existing object to which a new value is being assigned. The following code demonstrates this crucial difference:

A a;
A a2=a; //copy constructor call
A a3(a); //copy constructor call
A a4=a2=a1; //assignment operator for a2, the copy ctor for a4

As you can see, the fact that operator= is used doesn’t necessarily mean that the assignment operator is being called. The difference here is semantic not syntactic — if the program is instantiating a new object, it’s the copy constructor call. If the object isn’t new, the assignment expression translates into an assignment operator call.

It’s impossible to assign to an existing object a new value by using the copy construction syntax:

A a5(a); //copy constructor invocation
a5(a); //syntax error

The second line doesn’t compile because the compiler treats it as a function call, not as an assignment expression.

To conclude, syntax-wise, the operator= may denote either an assignment operation or a copy construction operation, depending on the left-hand argument.

Question: What do the implicitly-declared copy constructor and assignment operators do?

Answer: Contrary to a common belief, the assignment operator isn’t an original C++ concept. It dates back to ANSI C. C89 allows you to assign two instances of the same struct using =:

/*ANSI C89 code*/
struct A 
{
 int m;
 int n;
};

struct A a1; 
struct A a2={0, 10};
a1=a2; //memberwise copy

After the assignment, a1’s members have the same values as their respective a2’s members. C++ made some object-oriented adaptations to the C notion of assigning two structs. In C++, the implicitly declared assignment operator is a public and inline member function. C++ also guarantees member-wise assignment. That is, when you assign a2 to a1, the implementation conceptually performs the following operations:

a1.m=a2.m;
a1.n=a2.n;

The difference between member-wise copying and bitwise copying (such as a memcpy() call) is significant in two cases:

  • copying of subobjects
  • copying of padding bytes

If m and n were class objects with non-trivial copy constructors, the implicitly-declared assignment operator of A would be implicitly-defined. Such an implicitly-defined assignment operator invokes the copy constructor of each data member and base class subobject. However, even when a trivial assignment operator is used, its operation differs from a memcpy() call with respect to padding bytes. Let’s look at a concrete example:

struct Date
{
char day;
char month
//two padding bytes inserted here
int year;
};
Date epoch={1, 1, 1970};
Date d;
d=epoch;

On a typical 32-bit architecture, struct Date occupies a total of 8 bytes:

  • Two bytes for the first two members (one byte each)
  • Two bytes serve for padding
  • Four bytes for the member year.

When epoch is assigned to d, epoch’s padding bytes aren’t copied to d’s padding bytes. Thus, the respective members of d and epoch have identical values after the assignment but d and epoch aren’t necessarily bitwise identical because their padding bytes might differ. This isn’t just a piece of C++ trivia. Algorithms using memcmp() to test whether two objects are identical might conclude that two objects aren’t identical even if their respective members have identical values. Therefore, an overloaded operator== should use member-wise comparison even when applied to POD-objects.

The next part will discuss the do’s and don’ts of the copy constructor and assignment operator.

 

 

 

Copy Constructor and Assignment Operator FAQ, Part II

Last updated Oct 26, 2007.

The final part of this series discusses the benefits of compiler-generated copy constructor and assignment operator, reviews a questionable technique of implementing an assignment operator by means of invoking the copy constructor, and finally, it lists common design mistakes and bugs in the implementation of the copy constructor and the assignment operator.

Question: Can I Implement The Assignment Operator by Means of Invoking The Copy Constructor?

Answer: Yes, you can. However, it isn’t such a great idea. Some C++ gurus, journals and books in the early 1990s advocated the implementation of the assignment operator by means of calling the copy constructor. This idiom seemed like a good example of code reuse and “cuteness”. It works like this:

  • The assignment operator checks whether *this and its argument are not identical.
  • If the two objects are not identical, invoke the destructor of *this explicitly
  • Reuse the same memory address of this to copy construct a new object using placement new

The following code listing demonstrates this technique:

C& operator=(const C& rhs)
{
if (this !=&rhs)
{
 this->~C();//destroy self
 new (this) C(rhs);//reconstruct self using placement new and copy ctor
}
return *this;
}

This trick allegedly prevents code reduplication. However, it has some serious flaws. In order to work, C’s destructor must assign NULLify every pointer that it has deleted because the subsequent copy constructor call might delete the same pointers again when it reassigns a new value to char arrays. In addition, assigning objects is usually less expensive than a full-blown copy construction. This technique makes your assignment operator less efficient. The third and perhaps most compelling reason why you should avoid this idiom is its redundancy. Careful design can eliminate the need for writing the copy constructor and assignment operator manually. Let the compiler will generate them automatically!

Question: Can I trust the Compiler-Generated Copy Constructor and Assignment Operator?

Answer: Compiler-generated code is your best friend, provided that you adhere to good style OO practices and know the rules. As explained in part I, the compiler- generated copy constructor and assignment operator perform member-wise copying of user-declared data members. By replacing low-level datatypes — raw pointers and char arrays for instance — with their high-level Standard Library counterparts std::tr1::shared_ptr and std::string, not only are you eliminating the onerous bugs associated with manual resource management, you’re also guaranteeing that the compiler-generated copy constructor and assignment operator will do the right thing. Consider the following class:

class Person
{
char * s name;
...
Person();
~Person()
Person(const Person&);
Person& operator=(const Person& rhs);
};

By simply replacing the char * member with a string object, you eliminate the need for handwritten copy constructor and an assignment operator. Design your classes so that they can use the compiler-generated special member functions as much as possible.

Question: Which Common Design Mistakes Occur in User-Written Copy Constructor and Assignment Operator?

Answer: Many things can go wrong when users write these member functions on their own:

  • Forgetting to copy some of the object’s members
  • Using shallow copying instead of deep copying
  • Copying or assigning members in random order, for example copying the third member before the second, when the second depends on the third.

These are usually innocent human mistakes. However, there are also serious design mistakes that exhibit misunderstanding.

Using memcpy(). I’ll say it bluntly: memcpy() has no place in C++ as it is unaware of object semantics. It can be used only with POD types but class objects are rarely POD types anyway. Using sizeof(*this) to calculate the object’s size in a memcpy() call is particularly dangerous as it might overwrite the virtual pointer or do other foolish things with file descriptors, handles and pointers.

That said, the worst design mistake is defining empty copy constructors and assignment operators. When I discussed default constructors, I criticized a similar anti-pattern of defining a constructor that doesn’t do anything:

struct A
{
 A() {} //totally useless though nearly harmless
};

A user-defined constructor that doesn’t do anything is totally useless.As denigrated as this practice is, it’s not as detrimental as defining empty copy constructors and assignment operators, hoping that the compiler will work miracles:

struct S
{
 string name;
 int age;
 bool cached;
 S(string n, int a, bool c):name(n), age(a), cached(c) {}
 S(const S& rhs) {} //disatrous
 S& operator=(const S& rhs) {}//disastrous
};
int main()
{
S s1("Adam Smith", 20, false);
S s2(s1);//s1 members’ values aren’t copied at all! 
cout<<s2.age<<s2.cached<<endl; //garbage values
}

The user-defined copy constructor doesn’t contain any statements that copy rhs members to *this. Therefore, all POD members of s2 will have indeterminate (i.e., garbage) values. The string member will be empty.

When the assignment operator is implemented in the same manner, it will leave the target object intact:

S s1("Adam Smith", 20, false);
S s2("Robert Burns", 30, true);
s2=s1; //no effect on s2 members
cout<<s2.name<<endl; //still Robert Burns

The irony here is that the programmer shouldn’t have declared the copy constructor and assignment operator in the first place! The compiler-generated versions would work just fine.

Question: How Can I Prevent Object-Copying?

Answer: Declare the copy constructor and the assignment operator as private members, without defining them. Any statement that involves direct or indirect copying of that class will cause a compilation error. This technique isn’t exactly the picture of elegance, but in contemporary C++ there’s no other way to block copying inexpensively. When deleted and defaulted functions become available, you’ll be able to use a simpler and more elegant technique.

 

 

 

Destructors

Last updated Mar 1, 2004.

A destructor destroys an object of its class type. It takes no arguments and has no return type (not even void). const and volatile qualities are not applied on an object under destruction; therefore, const, volatile, or const volatile objects may have a destructor. If no user-defined destructor exists for a class, the implementation implicitly declares one. A destructor is trivial if it’s implicitly declared and if its direct-base classes and embedded objects have trivial destructors. Otherwise, the destructor is nontrivial. A destructor invokes the destructors of the direct base classes and member objects of its class. The invocation occurs in the reverse order of their construction.

Explicit Destructor Invocation

C++ automatically invoke destructors in the following cases:

  • For static objects at program termination
  • For a local object when the block in which the object is created exits
  • For a temporary object when the lifetime of the temporary object ends
  • For objects allocated on the free store using new, through the use of delete
  • During stack unwinding that results from an exception

You can also call a destructor explicitly. For example:

class C
{
public:
~C() {}
};

void destroy(C& c)
{
 c.C::~C(); //explicit destructor activation
}

A destructor can also be explicitly invoked from within a member function of its object:

void C::destroy()
{
 this->C::~C();
}

In particular, explicit destructor invocation is necessary for objects that were created by the placement new operator (placement new is discussed in the section “Memory Management“).

 

 

 

Destructors FAQ

Last updated Nov 16, 2007.

The destructor FAQ completes the FAQ trilogy which includes the Constructors FAQ and the Copy Constructor and Assignment Operator FAQ series. This Q&A format article explains key concepts of the C++ destructors and shows how to avoid common pitfalls and misunderstandings.

Question: Tell Me More About An Implicitly-Declared Destructor. When Is Such A Destructor Implicitly-Defined?

The compiler implicitly-declares a destructor in every class or struct that don’t have a user-declared destructor. Notice that unions cannot have a destructor at all, so the compiler doesn’t implicitly declare or define destructors in unions.

An implicitly-declared destructor is an inline public member of its class. A destructor is trivial if it is an implicitly-declared destructor and if:

  • All of the direct base classes of its class have trivial destructors and
  • For all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.

Otherwise, the destructor is non-trivial. A user-declared destructor isn’t trivial either, according to this definition. An implicitly-declared destructor is implicitly defined when it is used to destroy an object of its class type. A program is ill-formed if the class for which a destructor is implicitly defined has:

  • A nonstatic data member of class type (or array thereof) with an inaccessible destructor or
  • A base class with an inaccessible destructor.

Before the implicitly-declared destructor for a class is implicitly defined, all the implicitly-declared destructors for its base classes and its nonstatic data members shall have been implicitly defined as well. In simpler words, an implicitly-defined destructor has one purpose, namely, to invoke all non-trivial destructors of the base class(es) and embedded objects.

Question: Does a User-Defined Empty Destructor Have any Role?

Answer: Just as with empty user-defined constructors, some programmers insist on defining empty destructors in their class:

struct A
{
~A() {}
};

Such as destruct has no effect. However, it does change the semantics of struct A, making it a non-POD type. So while the destructor doesn’t perform any cleanup operation in this case, it may be used as a means of making a class a non-POD type. That said, I’m under the impression that most empty destructors have absolutely no purpose. They’re defined because programmers mistakenly believe that every class must have a destructor, which isn’t correct.

Question: I Defined A Constructor For My Class. Do I Have To Define A Destructor As Well?

Answer: Not really. Whether your class needs a destructor is determined by one criterion: does the object need to perform any cleanup operations before it’s destroyed. Most programmers are used to think of a destructor as a means for releasing a resource that was acquired during the object’s lifetime. That is correct. However, there are many objects that don’t acquire any resources at all, ever. Certain function objects for example never acquire any resources. Clearly, such objects don’t have to release any resources during destruction. Hence, they don’t need destructors. However, destructors may have different purposes such as writing to a log file, or printing a message on the user’s screen. To conclude, the decision whether to define a destructor isn’t automatic. It depends on the design and the use of the class. A class that has a user-declared constructor but doesn’t have a user-declared destructor isn’t necessarily an error.

Question: When Is It Necessary To Invoke A Destructor Explicitly? How Do I Do It?

Answer: In the majority of cases, you don’t have to invoke a destructor explicitly. In fact, most C++ programmers have never used this feature at all. An explicit destructor invocation is needed when an object is allocated on a predetermined memory address using placement-new. If you’re using placement-new to create an object, you must destroy that object explicitly before releasing its storage. In the Copy Constructor and Assignment FAQ series you saw a programming idiom that necessitates an explicit destructor invocation too.

To invoke a destructor explicitly, simply call it as an ordinary member function of the object. A destructor in C++ has a name (unlike a constructor, which doesn’t have a name). Here’s an example of an explicit destructor invocation:

A a;
a.~A(); //explicit destructor invocation

The name of the destructor is identical to its class name, preceded by a ~ sign. Another example:

A * pa= new A;
pa->~A();

The two explicit destructor invocations above use the non-qualified name of the destructor. You can also use the qualified name of the destructor in an explicit invocation:

A a;
a.A::~A(); //explicit destructor invocation
A * pa= new A;
pa->A::~A();

The qualified and non-qualified forms aren’t interchangeable. Whereas the non-qualified call will resolve the call (if the destructor is virtual), the qualified call is always resolved statically. Thus, of pa happens to be pointing to a derived object, the explicit destructor invocation with the qualified name will always be resolved as a call to A’s destructor (which is most likely a bug), whereas the non-qualified call will be resolved dynamically, invoking the derived object’s destructor first (which will in turn invoke base classes’ destructors).

Question: Can I Pass Parameters To A Destructor?

Answer: Destructors cannot take any parameters and they have no return type at all — not even void. Passing parameters to a destructor doesn’t make any sense because in the overwhelming majority of cases, the destructor is invoked implicitly. How can you pass arguments to a function that you don’t call directly? For a similar reason, a destructor cannot have a return type. The only two qualifiers allowed for a destructor are inline and virtual.

A destructor cannot be a const or volatile member function either. const and volatile semantics are not applied on an object under destruction, even if that object was declared const or volatile:

const A * pa = new A;
delete pa; // OK, *pa is no longer const once the destructor has been invoked.

Question: What Are the Common Destructor Pitfalls?

Answer: Apart from ordinary bugs, such as releasing a resource that has already been released or forgetting to release a resource, destructors are particularly vulnerable to two design mistakes.

  • Failing to declare a virtual destructor in a base class
  • Failing to implement a pure virtual destructor

A class that is meant to serve as a base class must define a virtual destructor, even if that destructor is empty. Remember: if you don’t declare a destructor, the compiler will implicitly declare (and in some cases, define) a destructor for that class. Such an implicitly-declared destructor is never virtual. In a similar vein, if a class doesn’t have a virtual destructor, you shouldn’t derive from it. STL containers are a classic example. Deriving a class from std::string is always a bad idea, as tempting as it may seem at first.

Pure virtual functions are meant to be overridden in a derived class. Normally, you don’t implement such functions (although it’s possible to do so) because pure virtual functions should never be invoked. And yet, pure virtual destructors are a special case. They must always be defined. The reason for that is the unique behavior or virtual destructors — a destructor in a derived class doesn’t override a base class destructor.

The definition of a pure virtual destructor must appear outside the class body:

struct interface
{
 virtual ~interface()=0;
};
interface::~interface(){}
This entry was posted in Case. Bookmark the permalink.

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s