1. Default empty constructors are not necessaryCompared to other constructors, the characteristic of a default empty constructor is that it can construct an object without requiring any parameters.However, we cannot always create objects this way in practice. For example, when creating an “ID card” object, the ID number is always required. Therefore, providing a default empty constructor in such classes is meaningless.2. Limitations of not providing a default empty constructorIf we do not provide a default empty constructor for such classes, it will impose some limitations on us during usage, which are listed and explained here:1. Limitations on creating object arraysAssuming there is a class “ID” representing an ID card, according to the rules, we do not provide a default empty constructor. Now, we attempt to create an array of “ID” objects:
ID ids[10]; // ErrorID *ids = new ID[10]; // Error
Both of these creation methods will fail, clearly indicating that they both use the default empty constructor of “ID”, which we have not provided.
① Non-heap array creation method for “ID”
ID ids[] = {ID(id1),ID(id2),ID(id3),ID(id4),ID(id5),...};
As long as parameters are provided when defining the array, non-heap arrays can be successfully created. However, this method is ineffective for heap array creation.
② Heap array creation method for “ID”
// Create an array of ID object pointers in the heapID** ids = new (ID*)[10];// Or create ID object pointers without placing them in the heapID* ids[10];// Each ID object pointer must be createdfor(int i=0;i<10;i++){ ids[i] = new ID(id);}
To create an array of “ID” objects in the heap, it can only be done indirectly through pointers. The final result is an array of “ID” object pointers, where each pointer points to an “ID” object in the heap.This method brings two issues. First, when releasing this array, we must release each “ID” object one by one. Second, we have separately stored the pointer array and the objects, which occupies more memory space.③ Creating a heap array of “ID” through raw memoryThis method can avoid occupying more memory. The specific principle is that we first allocate a block of memory that can hold all “ID” objects, read and use this block of memory as an array of “ID” objects, and finally create “ID” objects in this memory.
// Allocate a block of memory that can hold all "ID" objectsvoid *rawMemory = operator new[](10*sizeof(ID));// Read and use this block of memory as an array of "ID" objects (casting)ID *ids = static_cast<ID*>(rawMemory);// Create "ID" objects in this memoryfor(int i=0;i<10;i++){ new (&ids[i]) ID(id);}
The downside of this method is that it is overly complex, including its array release steps, which are not conventional:
// Destruct in reverse order of constructionfor(int i=9;i>=0;i--){ ids[i].~ID();}// Release the memory blockoperator delete[](rawMemory);
2. Limitations when using template collection classesWhen implementing template collection classes, we almost inevitably use arrays of template objects. If the template object does not have a default empty constructor, we must design carefully for this situation.The standard C++ vector, for example, has been designed with caution, but we cannot guarantee this situation with other template collection classes. In fact, most template collection classes lack this compatibility.3. Limitations of virtual base classesConsider that if a virtual base class does not have a default empty constructor, then whenever we create any derived class object, we must pass the parameters required by the virtual base class.This requires us to always remember the parameters needed for the virtual base class constructor, including their types and meanings.3. ConclusionNow looking back, not having a default empty constructor can be particularly troublesome. But does adding a default empty constructor to classes that do not need one solve the problem?In fact, there are still many issues; we need to be cautious about creating meaningless objects and check whether the objects are meaningful before each use. At the same time, we also need to handle cases of meaningless objects.Therefore, not providing unnecessary default empty constructors is relatively easier to manage.