Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Constructors FAQ, Part II

Last updated Jan 1, 2003.

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?