enable_from_this方法的使用与陷阱

enable_from_this 的使用与实现原理说明:

shared_from_this()是enable_shared_from_this的成员函数,返回shared_ptr;
注意的是,这个函数仅在shared_ptr的构造函数被调用之后才能使用。
原因是enable_shared_from_this::weak_ptr并不在构造函数中设置,而是在shared_ptr的构造函数中设置。

使用场合:当前类A的实例对象正被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数传给其他函数时,就需要传递一个指向此类A的实例对象自身的share_ptr。

错误的使用代码一:

#include<boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <iostream>
using namespace std;

class D: public boost::enable_shared_from_this<D>
{
public:
    D()
    {
        cout<<"D::D()"<<endl;
        boost::shared_ptr<D> p= shared_from_this();
    }    
};

int main()
{
    boost::shared_ptr<D> a(new D);
    return 0;    
}


程序编译通过,执行结果如下:
D::D()
terminate called after throwing an instance of 'boost::exception_detail::clone_impl >'
  what():  tr1::bad_weak_ptr
Aborted

说明在D的构造函数中调用shared_from_this(), 此时D的实例本身尚未构造成功,weak_ptr也就尚未设置,所以程序抛出tr1::bad_weak_ptr异常。


错误的使用代码二:


#include<boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <iostream>
using namespace std;

class D: public boost::enable_shared_from_this<D>
{
public:
    D()
    {
        cout<<"D::D()"<<endl;
    }
    
    void func()
    {
        cout<<"D::func()"<<endl;
        boost::shared_ptr<D> p= shared_from_this();
    }    
};

int main()
{
    D d;
    d.func();
    return 0;    
}


程序编译通过,执行结果如下:
D::D()
D::func()
terminate called after throwing an instance of 'boost::exception_detail::clone_impl >'
  what():  tr1::bad_weak_ptr
Aborted

失败原因分析:

在主函数main中,D的实例是在栈上构造,没有使用boost::shared_ptr 的构造方式,
所以boost::enable_shared_from_this中的weak_ptr所指的函数对象也就没有被赋值,
在调用d.func()中使用shared_from_this()函数时

----注:shared_from_this的函数实现 ------
    shared_ptr shared_from_this()
    {
        shared_ptr p( weak_this_ );
        BOOST_ASSERT( p.get() == this );
        return p;
    }
----注:shared_from_this的函数实现 ------

调用BOOST_ASSERT( p.get() == this );  失败,抛出以上异常。


最后,我们给出share_from_this()的正确使用例子:



#include<boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <iostream>
using namespace std;

class D: public boost::enable_shared_from_this<D>
{
public:
    D()
    {
        cout<<"D::D()"<<endl;
    }
    
    void func()
    {
        cout<<"D::func()"<<endl;
        boost::shared_ptr<D> p= shared_from_this();
    }    
};

int main()
{
    boost::shared_ptr<D> p(new D);
    p->func();
    return 0;    
}    


执行结果:
D::D()
D::func()
<think>好的,我现在需要详细介绍一下C++中的shared_ptr智能指针。首先,我应该从基本概念开始,解释什么是shared_ptr,它的作用和设计目的。然后,需要讨论它的核心机制,比如引用计数,所有权共享,以及如何避免内存泄漏。 接下来,我应该考虑构造和初始化的方法,比如直接构造、使用make_shared,或者通过其他指针或已有的shared_ptr来创建。这里要注意到make_shared的优势,比如内存分配效率。 然后,需要覆盖常用的成员函数,如use_count()、reset()、get()等,说明它们各自的用途和注意事项。特别是要强调不要轻易使用get()获取裸指针,以免引发悬挂指针的问题。 另外,自定义删除器的部分也很重要,说明为什么需要自定义删除器,以及如何使用它来处理特殊资源,比如文件指针或者malloc分配的内存。这里可能需要举例说明,比如传入一个lambda表达式作为删除器。 循环引用的问题必须提到,因为这是shared_ptr的常见陷阱。要解释循环引用是如何导致内存泄漏的,以及如何通过weak_ptr来打破循环。这部分需要结构清晰,可能用代码示例来演示问题及解决方案。 比较shared_ptr其他智能指针,比如unique_ptr和weak_ptr的区别,帮助用户理解何时选择哪种指针。要强调所有权模型的不同,unique_ptr的独占性,而shared_ptr的共享性。 还需要注意线程安全的问题,说明shared_ptr的引用计数操作是原子性的,但指向的对象本身并不是线程安全的。这部分可能需要更详细的解释,或者举例说明在多线程环境下如何正确使用。 最后,总结最佳实践,比如优先使用make_shared,避免循环引用,不要混合使用裸指针和智能指针等。同时,可以提到一些常见的错误用法,帮助用户避免陷阱。 在组织内容时,要确保结构清晰,分点阐述,每个部分有明确的标题,便于用户理解。同时,要注意语言的口语化,避免过于技术性的术语,必要时用例子辅助说明。另外,检查是否有遗漏的重要点,比如shared_ptr的性能开销,或者enable_shared_from_this的关系,如果有必要的话也应该简要提及。 现在,需要将这些内容整合成一个连贯的回答,确保覆盖所有关键点,同时保持逻辑流畅。可能需要先概述,再分章节详细说明,最后总结。注意用户要求用中文回答,所以术语要准确,但表达要自然易懂。</think>以下是对C++中`shared_ptr`智能指针的深度介绍,分模块逐步说明: --- ### 一、基本概念 `shared_ptr`是C++11引入的**共享所有权智能指针**,用于管理动态分配的内存。 - **核心特性**:通过引用计数机制,允许多个`shared_ptr`实例共享同一对象的所有权。 - **核心优势**:自动释放内存,避免内存泄漏;避免悬空指针问题。 --- ### 二、底层机制 #### 1. 引用计数 - 每个`shared_ptr`维护一个**控制块**(包含引用计数、弱引用计数、删除器、分配器等)。 - **规则**: - 构造或拷贝`shared_ptr`时,引用计数`+1`。 - 析构或`reset()`时,引用计数`-1`。 - 引用计数归零时,销毁对象并释放内存。 #### 2. 内存布局 ```cpp 对象内存 ───────────────┐ ↓ 控制块 [引用计数 | 弱引用计数 | 删除器 | 分配器] ``` --- ### 三、构造初始化 #### 1. 直接构造 ```cpp std::shared_ptr<int> p1(new int(42)); // 不推荐:可能因异常导致内存泄漏 ``` #### 2. 推荐方式:`make_shared` ```cpp auto p2 = std::make_shared<int>(42); // 推荐:单次内存分配(对象+控制块) ``` #### 3. 从其他指针构造 ```cpp std::shared_ptr<int> p3(p1); // 共享所有权,引用计数+1 ``` --- ### 四、核心成员函数 | 函数 | 功能 | 示例 | |--------------------|-----------------------------------|---------------------------| | `use_count()` | 返回当前引用计数 | `p1.use_count()` | | `reset()` | 释放所有权,引用计数-1 | `p1.reset()` | | `get()` | 返回裸指针(谨慎使用!) | `int* raw = p1.get()` | | `swap()` | 交换两个`shared_ptr`内容 | `p1.swap(p2)` | --- ### 五、自定义删除器 用于管理非`new`分配的资源(如文件句柄、`malloc`内存等): ```cpp // 示例:管理 malloc 分配的内存 std::shared_ptr<void> p4(malloc(100), [](void* ptr) { free(ptr); std::cout << "内存已释放"; }); ``` --- ### 六、循环引用问题 #### 1. 问题场景 ```cpp class Node { public: std::shared_ptr<Node> next; }; auto a = std::make_shared<Node>(); auto b = std::make_shared<Node>(); a->next = b; b->next = a; // 循环引用,引用计数永不为0 → 内存泄漏! ``` #### 2. 解决方案:`weak_ptr` ```cpp class Node { public: std::weak_ptr<Node> next; // 弱引用不增加计数 }; ``` --- ### 七、其他智能指针对比 | 类型 | 所有权模型 | 性能开销 | 适用场景 | |---------------|------------------|----------|------------------------| | `unique_ptr` | 独占所有权 | 无 | 单一所有者,资源转移 | | `shared_ptr` | 共享所有权 | 较高 | 多所有者共享资源 | | `weak_ptr` | 无所有权(观察) | 低 | 解决循环引用 | --- ### 八、线程安全性 - **引用计数操作**:原子操作,线程安全。 - **对象访问**:需手动加锁(裸指针相同)。 --- ### 九、最佳实践 1. **优先使用`make_shared`**:减少内存分配次数,避免异常安全问题。 2. **避免循环引用**:用`weak_ptr`打破循环。 3. **不要混用裸指针**: ```cpp int* raw = new int(10); std::shared_ptr<int> p5(raw); std::shared_ptr<int> p6(raw); // 错误!双重释放! ``` 4. **谨慎使用`get()`**:避免通过裸指针修改资源或延长生命周期。 --- ### 十、高级用法 #### 1. 别名构造(Alias Constructor) 允许`shared_ptr`指向某个对象的子部分,但共享整个对象的生命周期: ```cpp struct Data { int x, y; }; auto p7 = std::make_shared<Data>(); std::shared_ptr<int> p8(p7, &p7->x); // p8管理p7的x成员,但共享p7的控制块 ``` #### 2. `enable_shared_from_this` 在类的成员函数中安全获取自身的`shared_ptr`: ```cpp class MyClass : public std::enable_shared_from_this<MyClass> { public: void func() { auto self = shared_from_this(); // 正确获取shared_ptr } }; ``` --- ### 总结 `shared_ptr`通过引用计数实现自动内存管理,是C++中解决资源泄漏的核心工具之一。使用时需注意循环引用、线程安全和性能开销,合理结合`weak_ptr`和`unique_ptr`可构建高效、安全的内存管理体系。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值