In C++, we CANNOT make a constructor virtual. The reason is that C++ is a statically typed language, so the concept of a virtual constructor is a contradiction because the compiler needs to know the exact type at compile time to allocate the correct amount of memory and initialize the object correctly.
If we attempt to make a constructor virtual, the compiler will flag an error. In fact, apart from the inline keyword, no other keyword can be used in the constructor declaration.
Table of Content
Problem: Tightly Coupled Object Creation
In practical scenarios, you might want to create objects from a class hierarchy based on user input or runtime conditions. However, object creation in C++ is tightly coupled to the class type, which forces code modification and recompilation when new classes are added to the hierarchy.
For example, let’s consider a scenario where a class User is always creating an object of Derived1. If we later want to create a Derived2 object, we would need to modify the User class to instantiate Derived2. Such modifications require recompilation, which is a design flaw.
Example of Tight Coupling
Here’s an initial version of the code where the User class is tightly coupled with specific derived classes:
// C++ program to illustrate tight coupling problem
// in object creation
#include <iostream>
using namespace std;
// ----- LIBRARY CODE START FROM HERE -----
class Base {
public:
Base() {}
// Ensures to invoke actual object destructor
virtual ~Base() {}
// An interface
virtual void DisplayAction() = 0;
};
class Derived1 : public Base {
public:
Derived1() {
cout << "Derived1 created" << endl;
}
~Derived1() {
cout << "Derived1 destroyed" << endl;
}
void DisplayAction() {
cout << "Action from Derived1" << endl;
}
};
class Derived2 : public Base {
public:
Derived2() {
cout << "Derived2 created" << endl;
}
~Derived2() {
cout << "Derived2 destroyed" << endl;
}
void DisplayAction() {
cout << "Action from Derived2" << endl;
}
};
// ----- LIBRARY CODE ENDS HERE -----
class User {
public:
// Always creates Drived1
User() : pBase(nullptr) {
pBase = new Derived1();
// but what if Derived2 is required? - Add an if-else ladder (see next sample)
}
~User() {
if (pBase) {
delete pBase;
pBase = nullptr;
}
}
// Delegates to actual object
void Action() {
pBase->DisplayAction();
}
private:
Base *pBase;
};
int main() {
User *user = new User();
// Need Derived1 functionality only
user->Action();
delete user;
}
Output
Derived1 created Action from Derived1 Derived1 destroyed
In the above sample, assume that the hierarchies Base, Derived1 and Derived2 are part of library code. The class User is a utility class trying to make use of the hierarchy. The main function is consuming Base hierarchy functionality via the User class.
The User class constructor is creating Derived1 object, always. If the User's consumer (the main in our case) needs Derived2 functionality, the User needs to create "new Derived2()", which forces recompilation. Recompiling is a bad way of design, so we can opt for the following approaches to avoid recompilation.
Before going into the details, let us answer the question, who will dictate to create either of Derived1 or Derived2 object? Clearly, it is the consumer of the User class.
Methods for Decoupling
Following are the common methods to decouple the tightly coupled classes:
Modifying with an If-Else Ladder (Not Extensible)
One way to resolve the problem is by adding an if-else ladder based on some input to create either Derived1 or Derived2. However, this approach still has the same design flaw.
// C++ program to illustrate how to resolve
// the tight coupling problem using if-else
// statements in object creation
#include <iostream>
using namespace std;
// ----- LIBRARY CODE START FROM HERE -----
class Base {
public:
Base() {}
// Ensures to invoke actual object destructor
virtual ~Base() {}
// An interface
virtual void DisplayAction() = 0;
};
class Derived1 : public Base {
public:
Derived1()
cout << "Derived1 created" << endl;
~Derived1()
cout << "Derived1 destroyed" << endl;
void DisplayAction()
cout << "Action from Derived1" << endl;
};
class Derived2 : public Base {
public:
Derived2()
cout << "Derived2 created" << endl;
~Derived2()
cout << "Derived2 destroyed" << endl;
void DisplayAction()
cout << "Action from Derived2" << endl;
};
// ----- LIBRARY CODE ENDS HERE -----
class User {
public:
// Creates Derived1 or Derived2 based on input
User() : pBase(nullptr) {
// ID to distinguish between Derived1
// and Derived2
int input;
cout << "Enter ID (1 or 2): ";
cin >> input;
while ((input != 1) && (input != 2)) {
cout << "Enter ID (1 or 2 only): ";
cin >> input;
}
// Selecting the type of object using ID
// number
if (input == 1)
pBase = new Derived1;
else
pBase = new Derived2;
// What if Derived3 is being added to the
// class hierarchy?
}
~User() {
if (pBase) {
delete pBase;
pBase = nullptr;
}
}
// Delegates to actual object
void Action() {
pBase->DisplayAction();
}
private:
Base *pBase;
};
int main() {
User *user = new User();
// Need either Derived1 or Derived2 functionality
user->Action();
delete user;
}
Output
Enter ID (1 or 2): 2
Derived2 created
Action from Derived2
Derived2 destroyed
The above code is NOT open for extension, an inflexible design. In simple words, if the library updates the Base class hierarchy with new class Derived3, how can the User class create Derived3 object? One way is to update the if-else ladder that creates Derived3 object based on new input ID 3 but it again forces the consumers of the User class to recompile; again, bad (inflexible) design! Also, it won’t close the User class from further modifications due to Base extension.
The Factory Method (Best)
The problem is with the creation of objects. Addition of new class to the hierarchy is forcing the dependents of the User class to recompile. Can’t we delegate the action of creating objects to class hierarchy itself or to a function that behaves virtually? By delegating the object creation to class hierarchy (or to a static function), we can avoid the tight coupling between the User and Base hierarchy.
This method is known as better known as the Factory Method.
// C++ Program to illustrate the factory method
// for delegating the construction of the
// object to virtual static function
#include <iostream>
using namespace std;
// ----- LIBRARY CODE START FROM HERE -----
class Base {
public:
// The "Virtual Constructor"
static Base *Create(int id);
Base() {}
// Ensures to invoke actual object destructor
virtual ~Base() {}
// An interface
virtual void DisplayAction() = 0;
};
class Derived1 : public Base {
public:
Derived1() {
cout << "Derived1 created" << endl;
}
~Derived1() {
cout << "Derived1 destroyed" << endl;
}
void DisplayAction() override {
cout << "Action from Derived1" << endl;
}
};
class Derived2 : public Base {
public:
Derived2() {
cout << "Derived2 created" << endl;
}
~Derived2() {
cout << "Derived2 destroyed" << endl;
}
void DisplayAction() override {
cout << "Action from Derived2" << endl;
}
};
class Derived3 : public Base {
public:
Derived3() {
cout << "Derived3 created" << endl;
}
~Derived3() {
cout << "Derived3 destroyed" << endl;
}
void DisplayAction() override {
cout << "Action from Derived3" << endl;
}
};
// Create a factory that creates the object
// according to the requirement
class Factory {
public:
// Method that creates the object
Base* create(int id) {
// Just expand the if-else ladder, if new Derived class is created
// User code need not be recompiled to create newly added class objects
if( id == 1 )
return new Derived1;
else if( id == 2 )
return new Derived2;
else
return new Derived3;
}
};
// ----- LIBRARY CODE ENDS HERE -----
class User {
public:
User() : pBase(nullptr) {
// Receives an object of Base hierarchy at runtime
int input;
cout << "Enter ID (1, 2 or 3): ";
cin >> input;
while((input != 1) && (input != 2) && (input != 3)) {
cout << "Enter ID (1, 2 or 3 only): ";
cin >> input;
}
// Get object from the "Virtual Constructor"
Factory f;
pBase = f.create(input);
}
~User() {
if(pBase) {
delete pBase;
pBase = nullptr;
}
}
// Delegates to actual object
void Action() {
pBase->DisplayAction();
}
private:
Base *pBase;
};
// Consumer of User (UTILITY) class
int main() {
User *user = new User();
// Action required on any of Derived objects
user->Action();
delete user;
return 0;
}
Output
Enter ID (1, 2 or 3): 3
Derived3 created
Action from Derived3
Derived3 destroyed
The User class is independent of object creation. It delegates that responsibility to Base, and provides an input in the form of ID. If the library adds new class Derived4, the library modifier will extend the if-else ladder inside Create to return proper object. The consumers of User need not recompile their code due to extension of Base.
Note that the function Create is used to return different types of Base class objects at runtime. It acts like a virtual constructor, also referred to as Factory Method in pattern terminology.
The pattern world demonstrates different ways to implement the above concept. Also, there are some potential design issues with the above code. Our objective is to provide some insights into virtual construction, i.e., creating objects dynamically based on some input.