Memory leaks occur when dynamically allocated heap space is not properly released after use. C++ lacks built-in garbage collection mechanisms, and raw pointers cannot control the lifetime of the heap space they reference. This creates scenarios where a pointer's scope ends while the allocated memory persists, leading to resource leaks.
Smart Pointers Overview
Modern C++ platforms provide smart pointers as a solution to memory management challenges:
- Automatically release heap space when the pointer's lifetime ends
- Ensure exclusive ownership - only one smart pointer can manage a specific memory region
- Prevent pointer arithmetic and comparisons by design
Smart Pointer Design Strategy
The implementation leverages class templates to define pointer behaviors for various types. By overloading pointer-specific operators (-> and *), we can simulate native pointer behavior using objects.
Implemantation Details
Automatic Resource Cleanup
To ensure automatic memory release, the destructor must properly handle the raw pointer:
template<typename T>
class AutoPtr {
private:
T* resource;
public:
explicit AutoPtr(T* ptr = nullptr)
: resource(ptr) {}
T* operator->() const {
return resource;
}
T& operator*() const {
return *resource;
}
bool empty() const {
return resource == nullptr;
}
T* raw() const {
return resource;
}
~AutoPtr() {
delete resource;
}
};
Ownership Transfer Semantics
Implementing exclusive ownership requires careful handling in copy constructor and assignment operator:
AutoPtr(const AutoPtr<T>& other) {
resource = other.resource;
const_cast<AutoPtr<T>&>(other).resource = nullptr;
}
AutoPtr<T>& operator=(const AutoPtr<T>& other) {
if (this != &other) {
delete resource;
resource = other.resource;
const_cast<AutoPtr<T>&>(other).resource = nullptr;
}
return *this;
}
Preventing Pointer Operations
Pointer arithmetic and comparison are prevented by simply not overloading operators like ++, --, ==, !=, etc.
Complete Implementation
Header File
#ifndef AUTOPTR_H
#define AUTOPTR_H
namespace MemMgmt {
template<typename T>
class AutoPtr {
protected:
T* resource;
public:
explicit AutoPtr(T* ptr = nullptr)
: resource(ptr) {}
AutoPtr(const AutoPtr<T>& other) {
resource = other.resource;
const_cast<AutoPtr<T>&>(other).resource = nullptr;
}
AutoPtr<T>& operator=(const AutoPtr<T>& other) {
if (this != &other) {
delete resource;
resource = other.resource;
const_cast<AutoPtr<T>&>(other).resource = nullptr;
}
return *this;
}
T* operator->() const {
return resource;
}
T& operator*() const {
return *resource;
}
bool empty() const {
return resource == nullptr;
}
T* raw() const {
return resource;
}
~AutoPtr() {
delete resource;
}
};
}
#endif // AUTOPTR_H
Usage Example
#include <iostream>
#include "AutoPtr.h"
using namespace std;
using namespace MemMgmt;
class Sample {
public:
Sample() {
cout << "Sample constructed" << endl;
}
~Sample() {
cout << "Sample destroyed" << endl;
}
};
int main() {
AutoPtr<Sample> ptr1(new Sample());
AutoPtr<Sample> ptr2;
ptr2 = ptr1; // Ownership transfer
cout << "ptr1 empty: " << ptr1.empty() << endl;
cout << "ptr2 empty: " << ptr2.empty() << endl;
// ptr1++; // Compilation error - pointer arithmetic not supported
return 0;
}
- Pointer operators (-> and *) can be overloaded to create pointer-like behavior
- Operator overloading enables objects to substitute for raw pointers
- Smart pointers are designed specifically for heap-allocated memory management
- The primary value of smart pointers lies in preventing memory-related issues