Introduction
A constructor really makes your class simple and elegant. Constructors are such basic foundation for your code that a compiler provides it for you, by default. This is called as the default constructor. That is, in C++, if you write just a single line of code:
Class A{ };
the compiler provides you a default zero argument constructor, along with a destructor, a copy constructor, and a copy assignment operator.
Background
So, what is an Explicit Constructor?
In order to understand explicit construction, we need to understand implicit conversion first, and let’s see how it helps you while writing code, and sometimes how this feature is not desirable.
Using the code
Example 1: Consider the following traditional, simple, complex number class:
#include <iostream>
using std::cout;
using std::endl;
class complexNumbers {
double real, img;
public:
complexNumbers() : real(0), img(0) { }
complexNumbers(const complexNumbers& c) { real = c.real; img = c.img; }
complexNumbers( double r, double i = 0.0) { real = r; img = i; }
friend void display(complexNumbers cx);
};
void display(complexNumbers cx){
cout<<"Real Part: "<<cx.real<<" Imag Part: "<<cx.img<<endl;
}
int main() {
complexNumbers one(1);
complexNumbers five = 5;
display(one);
display(five);
return 0;
}
The class complexNumbers
is really simple; it contains two parts named real
and img
for a complex number’s real and imaginary parts. The code defines a default constructor, a copy constructor, and most importantly, defines another constructor which helps us to do implicit construction.
In main
, we first create an object of the class named “one
”, and then another named “five
”, both of these calls succeed because of the implicit conversation happening beneath. So, for the object “one
”, the “real
” part of the object becomes 1, and the “img” part becomes 0, and for the object “five
”, the “real
” part becomes 5 and the “img
” part becomes 0. Then, we print that complex number using a method named “display
”. Nice example indeed, and as of now, implicit conversion rocks. Given below is the output:
Real Part: 1 Imag Part: 0
Real Part: 5 Imag Part: 0
Now, consider this one:
#include <iostream>
using std::cout;
using std::endl;
class complexNumbers {
double real, img;
public:
complexNumbers() : real(0), img(0) { }
complexNumbers(const complexNumbers& c) { real = c.real; img = c.img; }
complexNumbers( double r, double i = 0.0) { real = r; img = i; }
friend void display(complexNumbers cx);
};
void display(complexNumbers cx){
cout<<"Real Part: "<<cx.real<<" Imag Part: "<<cx.img<<endl;
}
int main() {
complexNumbers one(1);
display(one);
display(300);
return 0;
}
A similar example, just added one more line in main
===> display(300);
. And, here we go, the output becomes:
Real Part: 1 Imag Part: 0
Real Part: 300 Imag Part: 0
Bang!!! That was really not expected. First, the code is itself confusing, what does display(300)
mean? 300 itself is never an object/instance of the class complexNumbers
that the method display
expects as an argument. So, how does this happen?
Since the method display
expects an object/instance of the class complexNumbers
as the argument, when we pass a decimal value of 300, an implicit conversion happens in-place, which in-turn transfers the decimal value of 300 to a temporary object of class complexNumbers
(and thereby assigns the value 300 to the real part of that temporary object).
How doe we overcome this situation??
Simple, force the compiler to create an object using explicit construction only, as given below:
#include <iostream>
using std::cout;
using std::endl;
class complexNumbers {
double real, img;
public:
complexNumbers() : real(0), img(0) { }
complexNumbers(const complexNumbers& c) { real = c.real; img = c.img; }
explicit complexNumbers( double r, double i = 0.0) { real = r; img = i; }
friend void display(complexNumbers cx);
};
void display(complexNumbers cx){
cout<<"Real Part: "<<cx.real<<" Imag Part: "<<cx.img<<endl;
}
int main() {
complexNumbers one(1);
display(one);
complexNumbers two =2;
display(200);
return 0;
}
Consider the following statement:
explicit complexNumbers( double r, double i = 0.0) { real = r; img = i; }
Here, we are forcing the compiler not to do an implicit conversion for this piece of code. Now, if the programmer puts a line containing an implicit conversation, the compiler returns back with an error:
bash-3.2$ g++ -g -o hello test2.cpp
test2.cpp: In function ‘int main()’:
test2.cpp:22: error: conversion from ‘int’ to non-scalar type ‘complexNumbers’ requested
test2.cpp:23: error: conversion from ‘int’ to non-scalar type ‘complexNumbers’ requested
From this point onwards, the programmers need to use this:
display(complexNumbers(200));