
Initialization and destruction
As shown previously, the creation of an object is a two-step process: memory allocation and initialization. Memory allocation is a result of an object declaration. C++ doesn't care about the initialization of variables; it allocates the memory (whether it is automatic or manual) and it's done. The actual initialization should be done by the programmer, which is why we have a constructor in the first place.
The same logic follows for the destructor. If we skip the declarations of the default constructor or destructor, the compiler should generate them implicitly, which it would also remove in case they are empty (to eliminate redundant calls to empty functions). The default constructor will not be generated by the compiler if any constructor with parameters is declared, including the copy constructor. We can force the compiler to implicitly generate the default constructor:
class Product {
public:
Product() = default;
// ...
};
We also can force it not to generate the compiler by using the delete specifier, as shown here:
class Product {
public:
Product() = delete;
// ...
};
This will prohibit default-initialized object declarations, that is, Product p; won't compile.
Object initialization happens on its creation. Destruction usually happens when the object is no longer accessible. The latter may be tricky when the object is allocated on the heap. Take a look at the following code; it declares four Product objects in different scopes and segments of memory:
static Product global_prod; // #1
Product* foo() {
Product* heap_prod = new Product(); // #4
heap_prod->name = "Sample";
return heap_prod;
}
int main() {
Product stack_prod; // #2
if (true) {
Product tmp; // #3
tmp.rating = 3;
}
stack_prod.price = 4.2;
foo();
}
global_prod has a static storage duration and is placed in the global/static section of the program; it is initialized before main() is called. When main() starts, stack_prod is allocated on the stack and will be destroyed when main() ends (the closing curly brace of the function is considered as its end). Though the conditional expression looks weird and too artificial, it's a good way to express the block scope.
The tmp object will also be allocated on the stack, but its storage duration is limited to the scope it has been declared in: it will be automatically destroyed when the execution leaves the if block. That's why variables on the stack have automatic storage duration. Finally, when the foo() function is called, it declares the heap_prod pointer, which points to the address of the Product object allocated on the heap.
The preceding code contains a memory leak because the heap_prod pointer (which itself has an automatic storage duration) will be destroyed when the execution reaches the end of foo(), while the object allocated on the heap won't be affected. Don't mix the pointer and the actual object it points to: the pointer just contains the value of the object, but it doesn't represent the object.
When the function ends, the memory for its arguments and local variables allocated on the stack will be freed, but global_prod will be destroyed when the program ends, that is, after the main() function finishes. The destructor will be called when the object is about to be destroyed.