Constructor overloading in c++

Last Edited By Krjb Donovan
Last Updated: Mar 11, 2014 07:51 PM GMT

QuestionEdit

Explain constructor overloading in c++ with example?

AnswerEdit

Constructor overloading is a special case of the more general technique of function overloading. In fact constructors are considered special class instance member functions.

Function overloading refers to the technique of allowing the existence of multiple functions with same name. In C++ such function overloads must differ by either the number or types of parameters (or be in another name space) so all the following functions (only their declarations are shown) may co-exist in C++:

   void f();
   int f(int);
   double f(double);
   long f(int, int);

But the following would not be allowed:

   int f();

As it differs from:

   void f();

only in its return type, which is not considered as a differentiating attribute of a function in C++ - which of course is not a concern for class instance constructor special functions as they may not specify a return type anyway.

( on the other hand placing the last function f in another namespace - i.e. one other than the global namespace like so:

   namespace n
   {
       int f();
   }

would be allowed as this f has the full name n::f. )

So overloading constructors means providing more than one constructor special member function to allow instances of some class to be created. In fact by default if no constructors are specified a class gets a default constructor taking no parameters and a copy constructor (if possible, if not then any compilation of code that causes the copy constructor to be called will fail). That is by default for some class C:

   class C
   {
   
   };

We have constructors provided by the compiler to allow us to create instances of C from no values and from other instances of C:

   C a_c;
   C a_copy_of_a_c( a_c );

Hence we get two constructor overloads by default!

However if we declare (and usually define as well!) any constructor for a class then the default constructor is not generated but a copy constructor is unless we explicitly declare one:

   class D
   {
       int value;
       
   public:
       D(int v)  
       : value(v)
       {
       }
   };

Hence we can create D objects from an integer value or from other D objects:

   D d(1);
   D copy_of_d( d );

But not with no parameters:

   D d; // ### Oops! Error! No generated D::D() constructor. ###

As an aside, it is usually a good idea to specify single parameter constructors as explicit to prevent the compiler using them to implicitly convert from values of the parameter type to the constructed type (unless of course we want this behaviour!):

   class D
   {
       int value;
       
   public:
       explicit D(int v)  
       : value(v)
       {
       }
   };

Of course we can provide our own set of overloaded constructors for our class types as necessary:

   class Node
   {
       Node *  next;
       int     value;
    
    public:
       explicit Node( int v )
       : next(0)  // null pointer to next Node - i.e. there is no next Node!
       , value(v)
       {
       }
       
       Node ( int v, Node * n )
       : next(n)  // pointer to next Node - there is one unless n==0
       , value(v)
       {
       }
   // ...
    private:
    // Declare private and do not define copy constructor and assignment operator
    // Makes Node not copyable or assignable
       Node( Node const & );
       Node & operator=( Node const & );
   };

Node has three constructors declared:

   - a copy constructor, that is private (explained below)
   - the public constructor taking a single int value, marked explicit
   - the public constructor taking an int value and a pointer to a next Node object.

Because we have declared one or more constructors explicitly the compiler generates no default constructor for the Node class. Also as we have explicitly declared a copy constructor no Node copy constructor is generated by the compiler either.

Hence the set of overloaded constructor special member functions are the three listed above.

That is all there is to constructor overloading really.

Now about the private copy constructor declaration and its associate the private assignment operator declaration. First I should mention that like copy constructors, the compiler will attempt generate an assignment operator function for a class if the class has not an explicit assignment operator declared. An assignment operator allows us to assign an instance of one object of a class to another:

   C a_c;
   C a_copy_of_a_c;
   
   a_copy_of_a_c = a_c; // a_c assigned to a_copy_of_a_c.

In fact it is probably better to say "The state of a_c assigned to a_copy_of_a_c".

Generally if instances of a class are copyable they can, and should be, assignable. Conversely, if we do not want instances of a class to be copyable then we usually also do not want them to be assignable either.

Now this presents us with a problem. If we do not want a class to be copy-assignable, how do we prevent the compiler 'helpfully' providing our class with definitions for these operations?

The idiomatic way to do this for the current version of C++ (the long awaited next version of the ISO C++ standard will probably allow us a bit more control over such things) is to declare these operations as private and never to provide definitions for them. This works on three levels. First by declaring them at all we prevent the compiler from generating implicit versions, second by making them private we ensure most uses are forbidden and the compiler will raise an error if they are used (the exceptions are uses in the class' own member functions and friends of the class) and third, if some use is not caught by the compiler then as there are no actual definitions the linker will raise an error when it tries to resolve the use to some actual code!

If you would like to see some less trivial examples of constructor overloading have a look at many of the classes or class templates in the C++ standard library, std::basic_string for example (OK this is a class template for creating specific string classes, but it still defines the constructors to be generated!):

   explicit basic_string(const Allocator& a = Allocator());
   basic_string(const basic_string& str, size_type pos = 0
               , size_type n = npos, const Allocator& a = Allocator()
               );
   basic_string(const charT* s, size_type n, const Allocator& a = Allocator());
   basic_string(const charT* s, const Allocator& a = Allocator());
   basic_string(size_type n, charT c, const Allocator& a = Allocator());
   template<class InputIterator>
   basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator());

Which comes from the ISO C++ standards document (section 21.3.2)

You will notice that many of the parameters have defaults specified, e.g.:

   explicit basic_string(const Allocator& a = Allocator());

Which can be used to create a new string from an allocator, passing in an instance to use or from no arguments at all (and therefore provides the default construction requirement), using a default of a default constructed instance of the allocator type specified for the specific type of std::basic_string, given by the class template type argument Allocator.

Hope this has helped.

Advertisement

©2024 eLuminary LLC. All rights reserved.