1. 智能指针的核心思想
- 共享指针:多个指针共享同一对象,通过引用计数自动释放内存
- 独占指针:单一所有权,禁止拷贝,支持移动语义
2. 共享指针(SharedPtr)实现剖析
引用计数类:线程安全的计数管理
class SharedCount {
public:
SharedCount() : count_{1} {} // 初始值为1
void add() { ++count_; } // 原子操作增加计数
void minus() { --count_; } // 原子操作减少计数
int get() const { return count_; }
private:
std::atomic<int> count_; // 原子类型保证线程安全
};
关键操作实现:
template <typename T>
class SharedPtr {
public:
// 构造函数
SharedPtr() : ptr_(nullptr), ref_count_(new SharedCount){}
SharedPtr(T* ptr) : ptr_(ptr), ref_count_(new SharedCount){}
// 拷贝构造:共享计数
SharedPtr(const SharedPtr& p) {
this->ptr_ = p.ptr_;
this->ref_count_ = p.ref_count_;
ref_count_->add(); // 引用计数+1
//std::cout << "拷贝构造!" << std::endl;
}
// 赋值操作符:处理自我赋值
SharedPtr& operator=(const SharedPtr& p) {
if (this != &p) { // 防止自我赋值
clean(); // 清理当前资源
this->ptr_ = p.ptr_;
this->ref_count_ = p.ref_count_;
ref_count_->add();
//std::cout << "赋值!" << std::endl;
}
return *this;
}
// 析构:计数归零时释放资源
~SharedPtr() {
clean();
}
int use_count() {
return ref_count_->get();
}
T* get() const {
return ptr_;
}
T* operator->() const {
return ptr_;
}
T& operator*() const {
return *ptr_;
}
operator bool() const {
return ptr_;
}
private:
void clean() {
if (ref_count_) {
ref_count_->minus();
if (ref_count_->get() == 0) { // 无引用时释放
if (ptr_) delete ptr_;
delete ref_count_;
}
}
}
private:
T* ptr_;
SharedCount* ref_count_;
};
3. 独占指针(UniquePtr)实现要点
禁用拷贝语义:
UniquePtr(const UniquePtr& p) = delete; // 禁用拷贝构造
UniquePtr& operator=(const UniquePtr& p) = delete; // 禁用拷贝赋值
移动语义支持:
// 移动构造:转移所有权
UniquePtr(UniquePtr&& p) noexcept {
ptr_ = p.ptr_;
p.ptr_ = nullptr; // 置空原指针
}
// 移动赋值:先释放现有资源
UniquePtr& operator=(UniquePtr&& p) noexcept {
if (this != &p) { // 处理自我移动
clean(); // 释放当前资源
ptr_ = p.ptr_;
p.ptr_ = nullptr;
}
return *this;
}
4. 完整代码及测试分析
#include <iostream>
#include <atomic>
class SharedCount {
public:
SharedCount() : count_{1}{}
void add() {
++count_;
}
void minus() {
--count_;
}
int get() const {
return count_;
}
private:
std::atomic<int> count_;
};
template <typename T>
class SharedPtr {
public:
// 构造函数
SharedPtr() : ptr_(nullptr), ref_count_(new SharedCount){}
SharedPtr(T* ptr) : ptr_(ptr), ref_count_(new SharedCount){}
// 拷贝构造:共享计数
SharedPtr(const SharedPtr& p) {
this->ptr_ = p.ptr_;
this->ref_count_ = p.ref_count_;
ref_count_->add(); // 引用计数+1
//std::cout << "拷贝构造!" << std::endl;
}
// 赋值操作符:处理自我赋值
SharedPtr& operator=(const SharedPtr& p) {
if (this != &p) { // 防止自我赋值
clean(); // 清理当前资源
this->ptr_ = p.ptr_;
this->ref_count_ = p.ref_count_;
ref_count_->add();
//std::cout << "赋值!" << std::endl;
}
return *this;
}
// 析构:计数归零时释放资源
~SharedPtr() {
clean();
}
int use_count() {
return ref_count_->get();
}
T* get() const {
return ptr_;
}
T* operator->() const {
return ptr_;
}
T& operator*() const {
return *ptr_;
}
operator bool() const {
return ptr_;
}
private:
void clean() {
if (ref_count_) {
ref_count_->minus();
if (ref_count_->get() == 0) { // 无引用时释放
if (ptr_) delete ptr_;
delete ref_count_;
}
}
}
private:
T* ptr_;
SharedCount* ref_count_;
};
template <typename T>
class UniquePtr {
public:
UniquePtr() : ptr_(nullptr) {}
UniquePtr(T* ptr) : ptr_(ptr){}
UniquePtr(const UniquePtr& p) = delete; // 禁用拷贝构造
UniquePtr& operator=(const UniquePtr& p) = delete; // 禁用拷贝赋值
UniquePtr(UniquePtr&& p) noexcept {
this->ptr_ = p.ptr_;
p.ptr_ = nullptr; // 置空原指针
std::cout << "移动构造!" << std::endl;
}
UniquePtr& operator=(UniquePtr&& p) noexcept {
if (this != &p) { // 处理自我赋值
clean(); // 释放当前资源
this->ptr_ = p.ptr_;
p.ptr_ = nullptr;
}
std::cout << "移动赋值!" << std::endl;
return *this;
}
T* get() const {
return ptr_;
}
T* operator->() const {
return ptr_;
}
T& operator*() const {
return *ptr_;
}
operator bool() const {
return ptr_;
}
~UniquePtr() {
clean();
}
private:
void clean() {
if (ptr_) delete ptr_;
}
T* ptr_;
};
class A {
public:
A() {
std::cout << "A()" << std::endl;
}
~A() {
std::cout << "~A()" << std::endl;
}
};
int main() {
// SharedPtr测试
SharedPtr<A> a(new A); // 输出:A()
{
SharedPtr<A> a2;
a2 = a; // 调用赋值操作符,计数+1
cout << a.use_count() << endl; // 输出:2
} // a2析构,计数-1
cout << a.use_count() << endl; // 输出:1
// UniquePtr测试
UniquePtr<A> b(new A); // 输出:A()
UniquePtr<A> b2(std::move(b)); // 移动构造,b失效
// b2离开作用域时输出:~A()
return 0;
}
5. 关键实现细节
- 线程安全:
std::atomic确保引用计数原子操作 - 自我赋值处理:
operator=中检查this != &p - 移动语义:
- 移动后置空原指针,避免双重释放
- 使用
noexcept声明不抛出异常
- 资源释放:
SharedPtr在计数归零时删除对象和计数器UniquePtr在析构时直接释放资源
6. 总结
- SharedPtr适用于共享所有权场景,通过引用计数自动管理生命周期
- UniquePtr轻量高效,适用于独占资源,支持移动语义
- 手写实现需注意线程安全、自我赋值和资源释放边界条件
通过实现智能指针,可深入理解RAII(资源获取即初始化)原则,提升内存管理能力。实际开发中推荐使用std::shared_ptr和std::unique_ptr,它们经过充分优化并严格遵循C++标准。