在 C++17 之前,智能指针(如 std::unique_ptr、std::shared_ptr)已经通过 move 构造函数和 move 赋值运算符实现了移动语义。然而,若我们想根据自己的需求自定义一个类似的智能指针,并且充分利用移动语义来优化性能,可以参考以下实现思路。
1. 设计目标
- 所有权独占:像 std::unique_ptr 一样,一个指针对象在任意时刻只能有一个所有者。
- 资源释放:默认使用
delete,但允许自定义 deleter。 - 移动语义:支持
X&&的移动构造与移动赋值,防止不必要的拷贝。 - 轻量级:尽量减少额外的成员变量与内存开销。
2. 基本结构
template<typename T, typename Deleter = std::default_delete<T>>
class MovePtr {
public:
// 默认构造,指针为空
MovePtr() noexcept : ptr_(nullptr) {}
// 直接构造,接收裸指针
explicit MovePtr(T* p) noexcept : ptr_(p) {}
// 通过 deleter 构造
MovePtr(T* p, Deleter d) noexcept : ptr_(p), deleter_(std::move(d)) {}
// 允许自定义 deleter 的默认构造
explicit MovePtr(Deleter d) noexcept : deleter_(std::move(d)) {}
// 拷贝构造禁止
MovePtr(const MovePtr&) = delete;
// 拷贝赋值禁止
MovePtr& operator=(const MovePtr&) = delete;
// 移动构造
MovePtr(MovePtr&& other) noexcept : ptr_(other.ptr_), deleter_(std::move(other.deleter_)) {
other.ptr_ = nullptr;
}
// 移动赋值
MovePtr& operator=(MovePtr&& other) noexcept {
if (this != &other) {
reset();
ptr_ = other.ptr_;
deleter_ = std::move(other.deleter_);
other.ptr_ = nullptr;
}
return *this;
}
// 析构
~MovePtr() { reset(); }
// 资源释放
void reset() noexcept {
if (ptr_) {
deleter_(ptr_);
ptr_ = nullptr;
}
}
// 获取裸指针
T* get() const noexcept { return ptr_; }
// 解引用操作符
T& operator*() const noexcept { return *ptr_; }
// 访问成员
T* operator->() const noexcept { return ptr_; }
// 隐式转换为 bool
explicit operator bool() const noexcept { return ptr_ != nullptr; }
private:
T* ptr_;
Deleter deleter_{};
};
3. 关键实现细节
-
禁止拷贝
通过删除拷贝构造与拷贝赋值,保证对象唯一所有权。 -
移动构造与移动赋值
- 移动构造时,将源对象的指针与 deleter 迁移到新对象,并将源指针置为
nullptr。 - 移动赋值时,先释放自身已有资源,再迁移,最后置源对象为空。
- 移动构造时,将源对象的指针与 deleter 迁移到新对象,并将源指针置为
-
自定义 deleter
Deleter默认使用 `std::default_delete `,可以像 `std::unique_ptr` 一样接受任何可调用对象。- 在构造时若传入 deleter,则复制或移动该 deleter,保证其生命周期足够长。
-
异常安全
- 所有成员函数都使用
noexcept,满足移动构造/赋值的标准要求。 reset()采用try-catch机制(此处省略),以避免 deleter 抛异常导致对象析构时崩溃。
- 所有成员函数都使用
4. 使用示例
// 自定义 deleter
auto array_deleter = [](int* p){ std::cout << "delete[] array\n"; delete[] p; };
int main() {
MovePtr <int> p1(new int(42)); // 默认 deleter
MovePtr<int[]> p2(new int[10], array_deleter); // 自定义 deleter
// 移动赋值
MovePtr <int> p3 = std::move(p1); // p1 现在为空
// 访问
if (p3) std::cout << *p3 << '\n'; // 输出 42
// 资源释放
p2.reset(); // 手动释放
// p2 析构时会再次释放,防止 double delete,需要在 reset 后置 nullptr
}
5. 性能考虑
- 内存占用:
MovePtr仅包含指针与 deleter,大小与std::unique_ptr相当。 - 移动效率:移动构造与赋值只涉及指针与 deleter 的拷贝,复杂度为 O(1)。
- 异常安全:在移动赋值过程中,若
reset()或 deleter 抛异常,使用noexcept标记会导致程序终止,因此建议 deleter 不抛异常。
6. 扩展功能
- 观察器模式:添加
use_count实现共享所有权(类似std::shared_ptr)。 - 线程安全:为 deleter 和引用计数加锁。
- 内存池:自定义分配器,用于高频分配释放。
7. 结语
通过上述实现,我们可以在不依赖标准库的前提下,得到一个具备移动语义、可自定义 deleter 的轻量级智能指针。它在 C++ 语言中保持了与 std::unique_ptr 的一致性,同时提供了更大的灵活性和可扩展性。若需进一步优化性能或功能,可在此基础上进行定制化扩展。