C++ smart pointers are tools that manage dynamically allocated memory through the RAII (Resource Acquisition Is Initialization) technique, which helps avoid common memory leak issues associated with manual memory management. Below, we detail its two core mechanisms: reference counting and custom deleters.
1. Reference Counting Principle
Reference counting is the fundamental mechanism for the automatic release of resources in smart pointers. The workflow is as follows:
-
When a smart pointer is created and points to a resource, the reference count of that resource is initialized to 1.
-
When the smart pointer is copied (e.g., assigned to another smart pointer) or passed as a parameter, the reference count is incremented by 1.
-
When the smart pointer is destroyed (e.g., goes out of scope) or reset to point to another resource, the reference count is decremented by 1.
-
When the reference count drops to 0, it indicates that no smart pointer is referencing that resource, at which point the resource is automatically released.
This mechanism ensures that the lifecycle of the resource is strictly tied to the number of smart pointers referencing it. The std::shared_ptr and std::weak_ptr in the C++ standard library are based on reference counting.
2. Custom Deleters
By default, smart pointers use the delete operator to release resources when the reference count reaches 0. However, for certain special resources, such as file handles or network connections, a custom release method may be required. In such cases, custom deleters can be used. Custom deleters allow users to specify a callable object (function, function object, or lambda expression) that will be called to perform specific cleanup operations when the resource is released.
3. Example Code
Below is a simple example demonstrating the use of reference counting and custom deleters:
#include <iostream>
#include <memory>// Custom resource class for demonstration
class Resource {
public:
Resource(int id) : id_(id) {
std::cout << "Resource " << id_ << " created" << std::endl;
}
~Resource() {
std::cout << "Resource " << id_ << " destroyed" << std::endl;
}
void use() const {
std::cout << "Using resource " << id_ << std::endl;
}
private:
int id_;
};
// Custom deleter function
void customDeleter(Resource* res) {
std::cout << "Custom deleting resource" << std::endl;
delete res;
}
int main() {
// Create a shared_ptr with a custom deleter
std::shared_ptr<Resource> ptr1(new Resource(1), customDeleter);
std::cout << "ptr1 use count: " << ptr1.use_count() << std::endl;
// Copy shared_ptr, reference count increases
std::shared_ptr<Resource> ptr2 = ptr1;
std::cout << "ptr1 use count: " << ptr1.use_count() << std::endl;
std::cout << "ptr2 use count: " << ptr2.use_count() << std::endl;
// Use resource
ptr1->use();
ptr2->use();
// Reset ptr2, reference count decreases
ptr2.reset();
std::cout << "After reset ptr2:" << std::endl;
std::cout << "ptr1 use count: " << ptr1.use_count() << std::endl;
// ptr1 goes out of scope, reference count becomes 0, resource is released
return 0;
}
4. Code Explanation
1. Reference Counting Demonstration:
-
ptr1 starts with a reference count of 1 upon creation.
-
ptr2 after copying ptr1, both have a reference count of 2.
-
ptr2.reset() makes ptr2 stop referencing the resource, and ptr1‘s reference count returns to 1.
-
When ptr1 goes out of scope, the reference count drops to 0, and the resource is released.
2. Custom Deleter Demonstration:
The customDeleter function is passed as the second parameter to std::shared_ptr.
When the resource is released, the customDeleter function is called first, followed by the actual resource release operation.
Through this approach, C++ smart pointers provide a safe and flexible memory management method that automatically manages resource lifecycles while accommodating various special resource release needs.