独占指针(unique_ptr
)是C++11标准引入的一种智能指针,用于独占管理动态分配对象的生命周期。unique_ptr
确保对象在同一时间只有一个所有者,防止对象被多个指针共享。下面是unique_ptr
的实现原理及其内存管理机制。
unique_ptr
的基本原理
-
独占所有权:
unique_ptr
独占对象的所有权,不能被复制,但可以移动。- 通过移动语义,可以将所有权从一个
unique_ptr
转移到另一个。
-
自动内存管理:
- 当
unique_ptr
被销毁时,它所管理的对象也会被销毁,自动释放内存。
- 当
实现原理
-
基本结构:
unique_ptr
是一个模板类,包含一个原始指针和一个删除器。- 删除器是一个可调用对象(如函数指针、函数对象或
std::default_delete
),用于在unique_ptr
销毁时释放对象。
-
构造与析构:
- 构造函数:接受一个原始指针,默认使用
std::default_delete
作为删除器。 - 析构函数:调用删除器释放对象。
- 构造函数:接受一个原始指针,默认使用
-
禁止复制:
unique_ptr
禁止复制构造和复制赋值操作。- 通过删除复制构造函数和复制赋值操作符来实现。
-
移动语义:
- 允许移动构造和移动赋值操作。
- 通过移动构造和移动赋值操作符将所有权转移到另一个
unique_ptr
。
代码示例
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass() { std::cout << "MyClass constructed\n"; }
~MyClass() { std::cout << "MyClass destroyed\n"; }
void sayHello() { std::cout << "Hello\n"; }
};
int main() {
std::unique_ptr<MyClass> ptr1(new MyClass());
ptr1->sayHello();
// std::unique_ptr<MyClass> ptr2 = ptr1; // 错误:不能复制 unique_ptr
std::unique_ptr<MyClass> ptr2 = std::move(ptr1); // 移动所有权
if (!ptr1) {
std::cout << "ptr1 is empty\n";
}
if (ptr2) {
ptr2->sayHello();
}
return 0;
}
输出结果
MyClass constructed
Hello
ptr1 is empty
Hello
MyClass destroyed
内存管理机制
-
独占所有权:
unique_ptr
在同一时间只能有一个所有者,禁止复制操作。- 通过移动操作将所有权转移。
-
自动释放内存:
- 当
unique_ptr
超出作用域或被销毁时,自动调用删除器释放对象内存。
- 当
总结
unique_ptr
提供了一种安全的、自动的内存管理方式,确保对象不会被多个指针共享。- 它通过禁止复制和允许移动操作实现独占所有权。
- 使用删除器在
unique_ptr
销毁时释放对象内存,防止内存泄漏。
代码实现
这里是一个简单的unique_ptr
的实现原理代码示例。这个示例包含了unique_ptr
的核心功能,包括独占所有权、移动语义以及自动内存管理。
#include <iostream>
#include <utility> // for std::move
template<typename T>
class UniquePtr {
private:
T* ptr; // 原始指针
public:
// 构造函数
explicit UniquePtr(T* p = nullptr) : ptr(p) {}
// 禁止复制构造函数
UniquePtr(const UniquePtr&) = delete;
// 禁止复制赋值操作符
UniquePtr& operator=(const UniquePtr&) = delete;
// 移动构造函数
UniquePtr(UniquePtr&& other) noexcept : ptr(other.ptr) {
other.ptr = nullptr; // 将源指针置为空
}
// 移动赋值操作符
UniquePtr& operator=(UniquePtr&& other) noexcept {
if (this != &other) {
delete ptr; // 释放当前持有的资源
ptr = other.ptr; // 转移所有权
other.ptr = nullptr; // 将源指针置为空
}
return *this;
}
// 析构函数
~UniquePtr() {
delete ptr; // 释放资源
}
// 重载 * 操作符
T& operator*() const {
return *ptr;
}
// 重载 -> 操作符
T* operator->() const {
return ptr;
}
// 获取原始指针
T* get() const {
return ptr;
}
// 释放所有权并返回原始指针
T* release() {
T* temp = ptr;
ptr = nullptr;
return temp;
}
// 重新设置指针
void reset(T* p = nullptr) {
if (ptr != p) {
delete ptr; // 释放当前持有的资源
ptr = p; // 设置新的指针
}
}
};
class MyClass {
public:
MyClass() { std::cout << "MyClass constructed\n"; }
~MyClass() { std::cout << "MyClass destroyed\n"; }
void sayHello() { std::cout << "Hello\n"; }
};
int main() {
UniquePtr<MyClass> ptr1(new MyClass());
ptr1->sayHello();
// UniquePtr<MyClass> ptr2 = ptr1; // 错误:不能复制 unique_ptr
UniquePtr<MyClass> ptr2 = std::move(ptr1); // 移动所有权
if (!ptr1.get()) {
std::cout << "ptr1 is empty\n";
}
if (ptr2.get()) {
ptr2->sayHello();
}
return 0;
}
代码说明
-
构造函数:
UniquePtr(T* p = nullptr) : ptr(p) {}
:构造函数初始化原始指针。
-
禁止复制:
UniquePtr(const UniquePtr&) = delete;
:禁止复制构造函数。UniquePtr& operator=(const UniquePtr&) = delete;
:禁止复制赋值操作符。
-
移动语义:
UniquePtr(UniquePtr&& other) noexcept
:移动构造函数,从其他UniquePtr
转移所有权,并将源指针置空。UniquePtr& operator=(UniquePtr&& other) noexcept
:移动赋值操作符,从其他UniquePtr
转移所有权,并将源指针置空。
-
析构函数:
~UniquePtr()
:析构函数释放资源。
-
智能指针接口:
T& operator*() const
:重载解引用操作符。T* operator->() const
:重载箭头操作符。T* get() const
:返回原始指针。T* release()
:释放所有权并返回原始指针。void reset(T* p = nullptr)
:重新设置指针,释放当前持有的资源。
输出结果
MyClass constructed
Hello
ptr1 is empty
Hello
MyClass destroyed
总结
UniquePtr
确保对象在同一时间只有一个所有者,防止多个指针共享。- 通过移动语义,可以将所有权从一个
UniquePtr
转移到另一个。 UniquePtr
自动管理对象生命周期,当智能指针超出作用域时自动释放资源。