独占指针(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_ptrstd::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::movetemplate<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_ptrUniquePtr<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
自动管理对象生命周期,当智能指针超出作用域时自动释放资源。