解决unique_ptr在不同继承层次转化指针和deleter的问题

本文介绍了一个改进版的UniquePtr模板类,用于解决多继承层次中unique_ptr使用不当导致的问题,并通过示例展示了如何正确地使用它来管理不同类型的对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

unique_ptr在多继承层次使用时,如果使用不当,容易出现
B* p = new D(); delete p;的问题
因此这里提出一个UniquePtr旨在正确处理析构。当然你需要付出一点小小的性能代价

包含文件

#include <memory>
template<typename T>
struct DeleteFuncs
{
 static void deleteFunc(void* obj) { delete (T*)obj; }
};

struct UniqueDeleter
{
 void(*m_deleter)(void*) = [](void*){};
 UniqueDeleter(void(*func)(void*)) : m_deleter(func){}

 void operator()(void* ptr){ m_deleter(ptr); }
};

template<class _Ty, class... _Types> inline
 typename std::enable_if<!_STD is_array<_Ty>::value,
 _STD unique_ptr<_Ty, UniqueDeleter> >::type MakeUnique(_Types&&... _Args)
{
 return (_STD unique_ptr<_Ty, UniqueDeleter>(new _Ty(_STD forward<_Types>(_Args)...), DeleteFuncs<_Ty>::deleteFunc));
}

template<typename T>
using UniquePtr = typename std::unique_ptr<T, UniqueDeleter>;

测试代码

struct B
{
    ~B()
    {
        printf("~B()\n");
    }
};

struct D : B
{
    ~D()
    {
        printf("~D()\n");
    }
};

void deleteB(void* b)
{
    printf("deleteB\n");
    delete (B*)b;
}
void deleteD(void* d)
{
    printf("deleteD\n");
    delete (D*)d;
}

void test()
{
    puts("");
    {
        UniquePtr<D> ptrD = MakeUnique<D>();
        UniquePtr<B> ptrB = std::move(ptrD);
    }
    puts("");
    {
        UniquePtr<B> ptrB{ new D, deleteD };
    }
    puts("");
    {
        UniquePtr<B> ptrB{ new B, deleteB };
    }
    puts("");
    {
        UniquePtr<B> ptrB{ new D, deleteD };
        UniquePtr<D> ptrD = { (D*)ptrB.release(), ptrB.get_deleter() };
    }
    puts("");
    {
        UniquePtr<B> ptrB{ new D, deleteD };
        UniquePtr<D> ptrD = std::move((UniquePtr<D>&) ptrB);
    }
    puts("");
    {
        UniquePtr<B> ptrB{ new D, deleteD };
        UniquePtr<D> ptrD = (UniquePtr<D>&&) ptrB;
    }
    puts("");
}
<think>我们首先回答为什么需要智能指针,然后解释unique_ptrshared_ptr不同之处。回答分为两部分:1.为什么需要智能指针?2. unique_ptrshared_ptr的区别然后按照要求提出相关问题。</think>### 为什么C++需要智能指针?`unique_ptr``shared_ptr`有何不同? #### 为什么需要智能指针? **根本原因**:解决传统指针的**内存泄漏****资源管理**问题。 **具体问题**: 1. **内存泄漏**:手动`new/delete`不匹配导致 ```cpp void risky_function() { int* ptr = new int[100]; if (error_occurred) return; // 内存泄漏! delete[] ptr; // 可能永远不会执行 } ``` 2. **异常安全问题**:异常抛出时delete可能被跳过 3. **所有权不明确**:多个指针指向同一资源时,谁负责释放? 4. **悬空指针**:资源释放后指针未置空 **解决方案**:智能指针通过**RAII(资源获取即初始化)** 技术: - 在构造时获取资源 - 在析构时自动释放资源 - 提供明确的所有权语义 --- #### `unique_ptr` vs `shared_ptr` | 特性 | `unique_ptr` | `shared_ptr` | |---------------------|---------------------------------------|---------------------------------------| | **所有权** | 独占所有权(不可复制) | 共享所有权(引用计数) | | **复制行为** | 禁止复制,允许移动(`std::move`) | 允许复制(增加引用计数) | | **性能开销** | 接近裸指针(零开销) | 有引用计数开销 | | **适用场景** | 单一所有者场景 | 多个所有者共享资源场景 | | **循环引用处理** | 不涉及 | 需配合`weak_ptr`避免内存泄漏 | ##### 代码示例 ```cpp #include <memory> #include <iostream> // unique_ptr 示例 void unique_ptr_demo() { std::unique_ptr<int> uptr(new int(42)); // std::unique_ptr<int> uptr2 = uptr; // 错误!不可复制 std::unique_ptr<int> uptr2 = std::move(uptr); // 所有权转移 std::cout << *uptr2 << "\n"; // 输出42 } // 自动释放内存 // shared_ptr 示例 void shared_ptr_demo() { std::shared_ptr<int> sptr1(new int(100)); { std::shared_ptr<int> sptr2 = sptr1; // 复制,引用计数=2 std::cout << *sptr1 << " " << *sptr2 << "\n"; // 输出100 100 } // sptr2析构,引用计数=1 } // sptr1析构,引用计数=0,释放内存 // 循环引用示例(问题) struct Node { std::shared_ptr<Node> next; ~Node() { std::cout << "Node destroyed\n"; } }; void cyclic_reference() { auto node1 = std::make_shared<Node>(); auto node2 = std::make_shared<Node>(); node1->next = node2; node2->next = node1; // 循环引用,内存泄漏! } // 引用计数永远不为0,析构函数不会被调用 int main() { unique_ptr_demo(); shared_ptr_demo(); cyclic_reference(); // 演示内存泄漏 return 0; } ``` #### 关键区别详解 1. **`unique_ptr`**: - 使用**移动语义**转移所有权 - 可作为函数返回值(编译器优化) - 支持自定义删除器 ```cpp auto file_deleter = [](FILE* f) { fclose(f); }; std::unique_ptr<FILE, decltype(file_deleter)> file(fopen("test.txt", "r"), file_deleter); ``` 2. **`shared_ptr`**: - **控制块**存储引用计数(分配额外内存) - 线程安全的引用计数(原子操作) - 必须配合`weak_ptr`解决循环引用: ```cpp struct SafeNode { std::weak_ptr<SafeNode> next; // 使用weak_ptr打破循环 }; ``` --- ### 最佳实践建议 1. 优先使用`unique_ptr`(默认选择) 2. 需要共享所有权时才用`shared_ptr` 3. 工厂函数返回`unique_ptr`: ```cpp std::unique_ptr<MyClass> create_object() { return std::make_unique<MyClass>(); } ``` 4. 使用`make_shared`替代`new`(减少内存分配次数)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值