在现代 C++ 开发中,智能指针是管理资源的首选工具。虽然标准库已经提供了 std::unique_ptr、std::shared_ptr 和 std::weak_ptr 等,但在某些特殊场景下我们可能需要自定义自己的智能指针。例如,想要在指针上附加额外的日志信息、实现线程安全的引用计数,或者在指针失效时执行自定义回调。下面,我们将逐步实现一个名为 MySmartPtr 的简易智能指针,并演示如何在其内部实现复制与移动语义。
1. 设计目标
- 单一拥有者:与
std::unique_ptr类似,MySmartPtr只允许一个实例拥有同一块资源。 - 自定义回调:在资源释放时执行用户提供的函数。
- 复制与移动:支持显式移动构造/赋值;复制构造/赋值被删除,防止误用。
- 异常安全:保证在异常抛出时不泄漏资源。
2. 基础框架
#include <iostream>
#include <functional>
#include <utility>
template <typename T>
class MySmartPtr {
public:
// 构造函数:接收裸指针和可选的销毁回调
explicit MySmartPtr(T* ptr = nullptr,
std::function<void(T*)> deleter = [](T* p){ delete p; })
: ptr_(ptr), deleter_(std::move(deleter)) {}
// 析构函数:调用回调释放资源
~MySmartPtr() { reset(); }
// 禁止复制
MySmartPtr(const MySmartPtr&) = delete;
MySmartPtr& operator=(const MySmartPtr&) = delete;
// 移动构造
MySmartPtr(MySmartPtr&& other) noexcept
: ptr_(other.ptr_), deleter_(std::move(other.deleter_)) {
other.ptr_ = nullptr;
}
// 移动赋值
MySmartPtr& operator=(MySmartPtr&& other) noexcept {
if (this != &other) {
reset(); // 先释放自己持有的资源
ptr_ = other.ptr_; // 转移指针
deleter_ = std::move(other.deleter_);
other.ptr_ = nullptr; // 让源对象为空
}
return *this;
}
// 访问指针
T* get() const noexcept { return ptr_; }
T& operator*() const noexcept { return *ptr_; }
T* operator->() const noexcept { return ptr_; }
// 资源释放
void reset(T* new_ptr = nullptr) noexcept {
if (ptr_) {
deleter_(ptr_);
}
ptr_ = new_ptr;
}
// 判断是否为空
explicit operator bool() const noexcept { return ptr_ != nullptr; }
private:
T* ptr_;
std::function<void(T*)> deleter_;
};
说明
- 构造函数默认使用
delete释放资源,但用户可以传入自定义回调,例如 `std::default_delete ` 或 `free`。 reset方法保证即使在异常发生时也能安全释放资源。- 移动构造/赋值使用
noexcept标记,确保在容器搬迁时不会抛异常。
3. 使用示例
struct Widget {
int id;
Widget(int i) : id(i) { std::cout << "Widget " << id << " constructed\n"; }
~Widget() { std::cout << "Widget " << id << " destructed\n"; }
};
int main() {
// 1. 创建智能指针
MySmartPtr <Widget> ptr1(new Widget(42));
std::cout << "ptr1 id: " << ptr1->id << "\n";
// 2. 移动 ptr1 到 ptr2
MySmartPtr <Widget> ptr2 = std::move(ptr1);
if (!ptr1) std::cout << "ptr1 is empty after move\n";
// 3. 自定义销毁回调
MySmartPtr <Widget> ptr3(new Widget(99),
[](Widget* w){
std::cout << "Custom delete for widget " << w->id << "\n";
delete w;
});
// 4. 资源释放
ptr2.reset(); // 立即销毁
// ptr3 在 main 结束时自动销毁,触发自定义回调
}
输出
Widget 42 constructed
ptr1 id: 42
ptr1 is empty after move
Widget 99 constructed
Widget 42 destructed
Custom delete for widget 99
Widget 99 destructed
4. 进一步扩展
- 线程安全:在
ptr_旁边加一个std::atomic<T*>或std::mutex,确保多线程访问时的安全。 - 可变引用计数:类似
std::shared_ptr,实现引用计数机制,使多实例共享同一资源。 - 与 RAII 结合:在类中使用
MySmartPtr成员,确保类对象的生命周期结束时自动释放资源。 - 自定义分配器:允许用户传入自定义内存分配器,实现内存池或对齐分配。
5. 小结
本文通过实现 MySmartPtr 展示了如何在 C++ 中手工管理资源,并实现复制与移动语义。虽然标准库已提供强大的智能指针,但在特殊需求下,自定义实现能够给你更多控制权。只需关注构造、析构、移动和异常安全即可。希望这份实现对你在实际项目中设计自定义资源管理器有所帮助。