Including meaningless default constructors affects the efficiency of classes, so avoiding them in classes guarantees fully-initialized objects, with the cost of some limits on how such classes can be used.
For many objects, there is no reasonable way to perform a complete initialization in the absence of outside information. Consider a class for company equipment in which the corporate ID number of the quipment is a mandatory constructor argument:
|
|
With default constructor
Some people believe all classes should include a default constructors, even if a default constructor doesn’t have enough information to fully initialize objects of the class. Adherents to this philosophy will modify EquipmentPiece
as follows:
|
|
There are two downsides after including a default constructor in a class where none was warranted:
- There’s no longer any guarantee that the fields of an
EquipmentPiece
object have been meaningfully initialized, so member functions must check if each object has validIDNumber
before using it. If not, clients must find a solution to deal with the situation - sometimes they simply throw an exception or terminates the program, which may degrade the overall quality of the software. - The meaningless default constructors affects the efficiency of classes: member functions have to include extra code to test object’s validness and deal with the failure tests, and clients of those functions have to pay for the time those tests take as well as the space the extra code occupies.
Without default constructor
If a class lacks a default constructor, there are restrictions on how you can use that class. Specifically, in three constexts:
- Creation of arrays
- Ineligible for some template-based container classes
- Virtual base classes
1. Creation of arrays
There is, in general, no way to specify constructor arguments for objects in arrays:
|
|
There are three ways to get around the restriction:
-
Provide the necessary arguments when array is defined
This solution works only for non-heap arrays:
1 2 3 4 5 6 7 8
int ID1, ID2, ID3, ..., ID10; // variables to hold equipment ID numbers ... EquipmentPiece bestPieces[] = { // fine, ctor arguments are provided EquipmentPiece(ID1); EquipmentPiece(ID2); ... EquipmentPiece(ID10); }
-
Use an array of pointers instead of an array of objects
1 2 3 4 5 6 7
typedef EquipmentPiece* PEP; // a PEP is a pointer to an EuipmentPiece PEP bestPieces[10]; // fine, no ctor called PEP *bestPieces = new PEP[10]; // fine, no ctor called for (int i = 0; i < 10; ++i) { bestPieces[i] = new EquipmentPiece( ID Number ); }
The disadvantages to this approach is:
- We have to remember to delete all the objects pointed to by the array. Otherwise there’s resource leak.
- The total amount of memory increases due to the extra space for the pointers
-
Use “placement
new
” (item 8)1 2 3 4 5 6 7 8 9 10
// allocate enough memory for an array of 10 EuipmentPiece obj. void *rawMemory = operator new[](10*sizeof(EquipmentPiece)); // make bestPieces point to the memory so it can be treated as an EquipmentPiece array EquipmentPiece *bestPieces = static_cast<EquipmentPiece*> (rawMemory); // construct the EquipmentPiece objects in the memory using placement new for (int i = 0; i < 10; ++i) { new (bestPieces+i) EquipmentPiece( ID Number ); }
This design avoids the spece penalty of extra pointers, but the downside is that we must manually call destructors on the objects in the array, then manually deallocate the raw memory by calling
operator delete[]
(item 8), which is unfamiliar by most programmers1:1 2 3 4 5 6
// destruct the objects in bestPieces in the inverse order for (int i = 9; i >= 0; --i) { bestPieces[i].~EquipmentPiece(); } // deallocate the raw memory operator delete[](rawMemory);
2. Ineligible for some template-based container classes
Some templates requires that the type used to instantiate the template proved a default constructor for purpose such as createing an array of the template parameter type inside the template:
|
|
Although careful template design can eliminate the need for a default constructor (such as vector
template), there are templates designed with that requirement. That being the case, classes without default constructors will be incompatible with such templates.
3. Virtual base classes
Arguments for virtual base class construcctors must be provided by the most derived class of the object being constructed. As a result, a virtual base class lacking a default constructor requires that all classes derived from that class must know about, understand the meaning of, and provide for the virtual base class’s constructors’ arguments, which is neigher expected not appreciated by authors of derived classes.
-
If we forget this requirement and use the normal array-deletion syntax
delete[] bestPieces
, the program will behave unpredictably, because deleting a pointer that didn’t come from thenew
operator is undefined. ↩︎