在 C++11 之后,智能指针(如 std::unique_ptr 和 std::shared_ptr)已经成为管理资源的标准工具。然而,有时你可能需要一个更轻量、功能更专一的指针来满足某些特定需求。本文将从零实现一个最简洁的自定义智能指针 SimpleUniquePtr,并展示它的基本用法、优势与局限。
1. 设计目标
- 独占所有权:类似
unique_ptr,不允许多个指针共享同一块资源。 - 简单无引用计数:避免额外的内存开销和线程安全问题。
- 兼容标准容器:实现
operator*、operator->、get()等,让它能直接与 STL 容器配合使用。 - 异常安全:确保在异常抛出时不泄露资源。
2. 基本实现
#pragma once
#include <utility>
#include <cstddef> // size_t
#include <iostream>
template <typename T>
class SimpleUniquePtr {
private:
T* ptr_; // 原始裸指针
public:
// 构造函数
explicit SimpleUniquePtr(T* ptr = nullptr) noexcept : ptr_(ptr) {}
// 禁止拷贝构造和拷贝赋值
SimpleUniquePtr(const SimpleUniquePtr&) = delete;
SimpleUniquePtr& operator=(const SimpleUniquePtr&) = delete;
// 移动构造
SimpleUniquePtr(SimpleUniquePtr&& other) noexcept : ptr_(other.ptr_) {
other.ptr_ = nullptr;
}
// 移动赋值
SimpleUniquePtr& operator=(SimpleUniquePtr&& other) noexcept {
if (this != &other) {
reset(); // 先释放自己的资源
ptr_ = other.ptr_;
other.ptr_ = nullptr;
}
return *this;
}
// 析构
~SimpleUniquePtr() {
reset();
}
// 重置指针
void reset(T* ptr = nullptr) noexcept {
if (ptr_ != ptr) { // 防止自我重置
delete ptr_;
ptr_ = ptr;
}
}
// 释放所有权
T* release() noexcept {
T* old = ptr_;
ptr_ = nullptr;
return old;
}
// 访问元素
T& operator*() const noexcept { return *ptr_; }
T* operator->() const noexcept { return ptr_; }
T* get() const noexcept { return ptr_; }
// 取指针是否为空
explicit operator bool() const noexcept { return ptr_ != nullptr; }
// 交换
void swap(SimpleUniquePtr& other) noexcept {
std::swap(ptr_, other.ptr_);
}
};
代码说明
- 构造:接收裸指针,默认
nullptr。 - 禁止拷贝:确保独占所有权。
- 移动:把所有权转移给新对象,旧对象置空。
- 析构:若指针非空则
delete。 - reset/release:实现
std::unique_ptr的常用成员函数。
3. 使用示例
#include "SimpleUniquePtr.h"
struct Person {
std::string name;
int age;
Person(const std::string& n, int a) : name(n), age(a) {
std::cout << "Person constructed: " << name << std::endl;
}
~Person() {
std::cout << "Person destructed: " << name << std::endl;
}
};
int main() {
SimpleUniquePtr <Person> p1(new Person("Alice", 30));
// 访问
std::cout << p1->name << " is " << p1->age << " years old.\n";
// 移动所有权
SimpleUniquePtr <Person> p2 = std::move(p1);
if (!p1) std::cout << "p1 is now empty.\n";
// 重置为新对象
p2.reset(new Person("Bob", 25));
// 释放所有权
Person* raw = p2.release();
std::cout << "Raw pointer obtained for: " << raw->name << "\n";
delete raw; // 记得手动删除
return 0;
}
运行结果示例:
Person constructed: Alice
Alice is 30 years old.
Person destructed: Alice
p1 is now empty.
Person constructed: Bob
Raw pointer obtained for: Bob
Person destructed: Bob
4. 与标准智能指针对比
| 功能 | SimpleUniquePtr |
std::unique_ptr |
|---|---|---|
| 引用计数 | 无 | 无 |
| 自定义删除器 | 不支持 | 支持 |
operator[] |
不支持 | 支持(当指向数组) |
emplace |
不支持 | 支持 |
make_unique |
手动 new |
std::make_unique |
| 线程安全 | 仅在单线程 | 线程安全(移除后) |
优势
- 代码量少,易于嵌入项目的早期阶段。
- 对资源的占用更小,没有额外的引用计数对象。
局限
- 无法像
unique_ptr那样自定义删除器,无法直接管理数组。 - 缺乏
std::make_unique、std::make_shared等便利函数。
5. 扩展思路
- 自定义删除器:在模板中加入删除器类型参数 `typename Deleter = std::default_delete `,并在 `reset`、析构时调用。
- 数组管理:重载
operator[]并在构造时记录数组长度。 - 异常安全:在
reset时使用try-catch,保证异常不泄露资源。 - 与 STL 兼容:实现
get()、operator bool()以支持std::vector<SimpleUniquePtr<T>>等。
6. 结语
虽然标准库已经提供了功能强大的智能指针,但在一些特殊场景(如轻量化、教学演示、或对标准库不可用的环境)下,手写一个简单的自定义智能指针既能帮助理解所有权语义,又能让代码更贴合需求。希望本文的实现与示例能为你提供一个快速入门的参考。祝编码愉快!