Keywords: C++ | object instantiation | memory management
Abstract: This article provides an in-depth analysis of two object instantiation methods in C++: automatic storage duration and dynamic storage duration. It explains constructor invocation, memory management mechanisms, and lifetime control, detailing why automatic objects call destructors automatically while dynamic objects require manual deletion. Includes corrected code examples demonstrating proper memory management practices.
Fundamental Concepts of Object Storage Duration
In C++ programming, the method of object instantiation directly affects its storage duration type and memory management approach. Automatic storage duration objects are allocated on the stack with their lifetime automatically managed by scope, while dynamic storage duration objects are allocated on the heap and require explicit lifetime control by the programmer.
Instantiation of Automatic Storage Duration Objects
Creating objects using Obj o1("Hi\n"); results in automatic storage duration. This instantiation method allocates memory on the stack, and the object is automatically destroyed when it goes out of scope. The compiler automatically inserts destructor call code to ensure proper resource cleanup.
Instantiation of Dynamic Storage Duration Objects
Creating objects using Obj* o2 = new Obj("Hi\n"); results in dynamic storage duration. This instantiation method allocates memory on the heap, and the object does not automatically destruct—it must be explicitly released using the delete keyword. Dynamic storage duration objects offer more flexible lifetime control but also increase the risk of memory leaks.
Comparison of Destructor Invocation Mechanisms
Automatic storage duration objects automatically invoke their destructors when leaving scope, as the compiler handles cleanup. Dynamic storage duration objects only trigger their destructors when explicitly called with delete. This difference stems from the distinct memory management mechanisms of the two storage duration types.
Memory Management Issues in Original Code
The original code contains a critical issue in the destructor: delete str; attempts to free a string literal, which constitutes undefined behavior. The string literal "Hi\n" resides in the program's read-only data segment and should not be deleted. The correct approach is to only delete memory that was allocated via new.
Corrected Code Example
class Obj {
char* str;
public:
Obj(const char* s) {
str = new char[strlen(s) + 1];
strcpy(str, s);
cout << str;
}
~Obj() {
cout << "Done!\n";
delete[] str;
}
};
Guidelines for Choosing Application Scenarios
In most cases, prefer automatic storage duration objects due to their safer memory management and higher efficiency. Consider dynamic storage duration objects only in specific scenarios: when precise control over object lifetime is needed, when creating arrays with runtime-determined sizes, or when objects need to be shared across multiple scopes.
Best Practices for Memory Management
Adhere to the principle of "who allocates, who deallocates." Objects should only be responsible for freeing memory they allocated themselves, avoiding management of externally provided resources. For dynamically allocated objects, ensure every new has a corresponding delete, and consider using smart pointers to prevent memory leaks.