When an exception occurs in a constructor during a call to new, a memory leak happens. How can this be handled?
sobes.tech AI
Answer from AI
If an exception occurs in the constructor of an object created with new, the memory allocated for the object itself using operator new is not automatically released. This leads to a memory leak.
To solve this problem, approaches based on RAII (Resource Acquisition Is Initialization) can be used:
-
Smart Pointers: Using
std::unique_ptrorstd::shared_ptr. Memory is managed by a "smart" pointer, which releases the resource (memory) in its destructor, even if the constructor of the object it points to throws an exception.#include <memory> #include <stdexcept> class MyClass { public: MyClass() { // Simulate an exception in constructor throw std::runtime_error("Constructor exception"); } }; void func_with_leak_potential() { // Without smart pointer, memory leaks if constructor throws MyClass* obj = new MyClass(); // If MyClass() constructor throws, delete is not called delete obj; } void func_with_no_leak() { // Using std::unique_ptr // Memory allocated for MyClass will be released by unique_ptr's destructor // even if MyClass constructor throws std::unique_ptr<MyClass> obj = std::make_unique<MyClass>(); } -
Helper Classes/Scopes: Creating small classes that manage resources in their constructors and destructors. In the context of a constructor that may throw, such a class can temporarily own a resource (e.g., a raw pointer) and free it in its destructor if the main object's constructor fails.
#include <stdexcept> #include <iostream> // Helper class for managing raw pointer class PtrGuard { private: int* ptr_; bool released_; public: PtrGuard(int* p) : ptr_(p), released_(false) {} ~PtrGuard() { if (!released_ && ptr_) { std::cout << "Deleting ptr_ from Guard\n"; delete ptr_; } } int* get() const { return ptr_; } void release() { released_ = true; } // Transfer ownership }; class MyClassWithResource { private: int* resource_; public: MyClassWithResource() : resource_(nullptr) { // Allocate resource resource_ = new int(10); // Use Guard to manage resource until full construction PtrGuard guard(resource_); // Simulate exception after resource allocation // If exception occurs here, guard.release() is not called, // and resource will be freed in guard's destructor throw std::runtime_error("Constructor part exception"); // If no exception, "transfer ownership" to MyClassWithResource guard.release(); // resource_ now managed by MyClassWithResource's destructor } ~MyClassWithResource() { if (resource_) { std::cout << "Deleting resource_ from MyClassWithResource\n"; delete resource_; } } }; void func_with_no_leak_with_guard() { try { MyClassWithResource* obj = new MyClassWithResource(); // If no exception, remember to delete delete obj; } catch (const std::runtime_error& e) { std::cerr << "Caught exception: " << e.what() << std::endl; } }
Using smart pointers (std::unique_ptr and std::shared_ptr) is more idiomatic in modern C++ as they provide reliable and less verbose solutions for resource management in exception scenarios. Using std::make_unique (C++14 and above) or std::make_shared is also safe regarding exceptions, as memory allocation and constructor calls happen atomically.