c++ shared_ptr和weak_ptr结合应用实验

本文通过实例展示了C++中shared_ptr和weak_ptr在释放对象和解决交叉引用问题上的作用,以及如何使用weak_ptr避免内存泄露。

1.概要

  • shared_ptr和weak_ptr的reset实验
  • 用weak_ptr化解shared_ptr交叉引用的问题
  • std::unique_ptr<int> p2 = std::move(p1); // p1 变为空,p2 拥有对象的所有权

2.代码

#include <iostream>
using namespace std;

template<typename T>
void fun(weak_ptr<T> wp) {
    if (wp.expired()) {
        cout << "Object has been released\n";
    }
    else {
        cout << "Object not released\n";
    }
}
namespace t1 {
    void test() {
        cout << "--- Release with shared_ptr -----------------------------\n";
        shared_ptr<int> sh_ptr = make_shared<int>();
        weak_ptr<int> wp(sh_ptr);
        fun<int>(wp);//结果:Object not released
        sh_ptr.reset();
        fun<int>(wp);//结果:Object has been released
    }
}
namespace t2 {
    void test() {
        cout << "--- Release with weak_ptr -----------------------------\n";
        shared_ptr<int> sh_ptr = make_shared<int>();
        weak_ptr<int> wp(sh_ptr);
        fun<int>(wp);//结果:Object not released
        //sh_ptr.reset();
        wp.reset();
        fun<int>(wp);//结果:Object has been released
    }
}

namespace t3 {
    class B;
    class A
    {
    public:
        A() {
            cout << "A constructor call\n";
        }
        ~A() {
            cout << "A Destructor call\n";
        }
        shared_ptr<B> sh_ptr_b;
    };
    class B
    {
    public:
        B() {
            cout << "A constructor call\n";
        }
        ~B() {
            cout << "A Destructor call\n";
        }
        shared_ptr<A> sh_ptr_a;
    };
    void test() {
        //这个实验会让交叉引用发生,导致对象无法析构
        cout << "--- Cross references cannot be destructed -----------------------------\n";
        shared_ptr<A> sh_ptr_a = make_shared<A>();
        shared_ptr<B> sh_ptr_b = make_shared<B>();
        cout << "ptr_a use_count: " << sh_ptr_a.use_count() << endl;
        cout << "ptr_b use_count: " << sh_ptr_b.use_count() << endl;
        sh_ptr_a.get()->sh_ptr_b = sh_ptr_b;
        sh_ptr_b.get()->sh_ptr_a = sh_ptr_a;
        //这里因为发生了交叉引用,所以对象最终不会析构,会发生内存泄露
        cout << "ptr_a use_count: " << sh_ptr_a.use_count() << endl;
        cout << "ptr_b use_count: " << sh_ptr_b.use_count() << endl;
    }
/*
运行结果:最后没有调用析构函数
A constructor call
A constructor call
ptr_a use_count: 1
ptr_b use_count: 1
ptr_a use_count: 2
ptr_b use_count: 2
*/
}
namespace t4 {
    class B;
    class A
    {
    public:
        A() {
            cout << "A constructor call\n";
        }
        ~A() {
            cout << "A Destructor call\n";
        }
        //shared_ptr<B> sh_ptr_b;
        //这是把shared_ptr<B>换成了weak_ptr<B>就化解了交叉引用
        weak_ptr<B> sh_ptr_b;
    };
    class B
    {
    public:
        B() {
            cout << "A constructor call\n";
        }
        ~B() {
            cout << "A Destructor call\n";
        }
        shared_ptr<A> sh_ptr_a;
    };
    void test() {
        //交叉引用的问题会得到解决
        cout << "--- Cross reference problem solved -----------------------------\n";
        shared_ptr<A> sh_ptr_a = make_shared<A>();
        shared_ptr<B> sh_ptr_b = make_shared<B>();
        cout << "ptr_a use_count: " << sh_ptr_a.use_count() << endl;
        cout << "ptr_b use_count: " << sh_ptr_b.use_count() << endl;
        sh_ptr_a.get()->sh_ptr_b = sh_ptr_b;
        sh_ptr_b.get()->sh_ptr_a = sh_ptr_a;
        //这里weak_ptr<B>会对这种引用做相应的处理所以 最终对象可以析构 不会发生内存泄露
        cout << "ptr_a use_count: " << sh_ptr_a.use_count() << endl;
        cout << "ptr_b use_count: " << sh_ptr_b.use_count() << endl;

    }
/*
测试结果:调用了析构函数
A constructor call
A constructor call
ptr_a use_count: 1
ptr_b use_count: 1
ptr_a use_count: 2
ptr_b use_count: 1
A Destructor call
A Destructor call*/
}
int main()
{
    t1::test();
    t2::test();
    t3::test();
    t4::test();
    cout << "Hello World!\n";
}

3.运行结果

--- Release with shared_ptr -----------------------------
Object not released
Object has been released
--- Release with weak_ptr -----------------------------
Object not released
Object has been released
--- Cross references cannot be destructed -----------------------------
A constructor call
A constructor call
ptr_a use_count: 1
ptr_b use_count: 1
ptr_a use_count: 2
ptr_b use_count: 2
--- Cross reference problem solved -----------------------------
A constructor call
A constructor call
ptr_a use_count: 1
ptr_b use_count: 1
ptr_a use_count: 2
ptr_b use_count: 1
A Destructor call
A Destructor call

4.关联知识 

1.c++有几种智能指针?对比说明,举例

在C++中,主要有四种智能指针,它们是:std::auto_ptr(已被废弃)、std::unique_ptrstd::shared_ptr 和 std::weak_ptr。下面将分别介绍这些智能指针的特点和使用场景。

1. std::auto_ptr(已废弃)

std::auto_ptr 是C++98和C++03标准库中的智能指针,但由于其设计存在缺陷(如拷贝语义),已在C++11中被废弃,并在C++17中被完全移除。

  • 特点
    • 管理单个对象的所有权。
    • 拷贝时会转移所有权,导致源指针变为空。
    • 不适合用于容器或在多线程环境中使用。
  • 示例

    std::auto_ptr<int> p1(new int(10));  
    std::auto_ptr<int> p2 = p1; // p1 变为空,p2 拥有对象的所有权

2. std::unique_ptr

std::unique_ptr 是C++11引入的智能指针,用于替代 std::auto_ptr

  • 特点
    • 独占对象的所有权,不能复制,只能通过移动语义转移所有权。
    • 支持删除器(deleter),可以自定义删除逻辑。
    • 适用于管理单个对象的生命周期。
  • 示例

    std::unique_ptr<int> p1(new int(10));  
    std::unique_ptr<int> p2 = std::move(p1); // p1 变为空,p2 拥有对象的所有权

3. std::shared_ptr

std::shared_ptr 也是C++11引入的智能指针,用于共享对象的所有权。

  • 特点
    • 多个 std::shared_ptr 实例可以共享同一个对象。
    • 使用引用计数来管理对象的生命周期,当最后一个 std::shared_ptr 被销毁时,对象才会被删除。
    • 支持删除器(deleter)。
    • 线程安全(引用计数的增减是原子的)。
  • 示例

    std::shared_ptr<int> p1(new int(10));  
    std::shared_ptr<int> p2 = p1; // p1 和 p2 共享同一个对象

4. std::weak_ptr

std::weak_ptr 是为了配合 std::shared_ptr 而设计的,用于解决循环引用问题。

  • 特点
    • 不能独立拥有对象,必须从一个 std::shared_ptr 或另一个 std::weak_ptr 创建。
    • 不影响对象的引用计数,不会阻止对象被销毁。
    • 可以用于检查对象是否还存在,并获取一个 std::shared_ptr 来访问对象。
  • 示例

    std::shared_ptr<int> sp = std::make_shared<int>(10);  
    std::weak_ptr<int> wp = sp; // 不会增加引用计数  
    if (auto locked = wp.lock()) { // 检查对象是否还存在  
        // locked 是一个 shared_ptr,可以安全访问对象  
    }

对比总结

  • 所有权管理
    • std::unique_ptr:独占所有权,不可共享。
    • std::shared_ptr:共享所有权,通过引用计数管理。
    • std::weak_ptr:不管理所有权,仅用于引用 std::shared_ptr
  • 生命周期
    • std::unique_ptr:生命周期由单个 unique_ptr 管理。
    • std::shared_ptr:生命周期由所有 shared_ptr 共享管理。
    • std::weak_ptr:不影响生命周期管理。
  • 适用场景
    • std::unique_ptr:适用于需要独占所有权的场景。
    • std::shared_ptr:适用于需要共享所有权的场景。
    • std::weak_ptr:适用于需要引用 std::shared_ptr 但不增加引用计数的场景,常用于解决循环引用问题。

选择适当的智能指针,可以帮助你更好地管理动态内存和资源,避免内存泄漏和其他资源管理问题。

2.c++有几种智能指针?对比说明 

在C++中,智能指针是一种管理动态分配内存的类模板,用于自动处理内存释放,防止内存泄漏。C++11标准引入了三种主要的智能指针:std::unique_ptrstd::shared_ptr 和 std::weak_ptr。下面是对这三种智能指针的对比说明:

1. std::unique_ptr

  • 独占所有权std::unique_ptr 拥有对其管理对象的独占所有权。同一时间只能有一个 std::unique_ptr 拥有该对象。
  • 不可复制:由于其独占性,std::unique_ptr 不允许复制(拷贝构造函数和赋值操作符被删除)。但可以通过 std::move 转移所有权。
  • 销毁对象:当 std::unique_ptr 超出作用域或被销毁时,它所管理的对象也会被自动销毁(调用 delete)。
  • 定制删除器:可以指定自定义的删除器来控制对象的销毁方式。

2. std::shared_ptr

  • 共享所有权std::shared_ptr 允许多个智能指针共享同一个对象。对象会在最后一个 std::shared_ptr 被销毁时自动释放。
  • 引用计数:内部使用引用计数机制来跟踪有多少个 std::shared_ptr 共享同一个对象。
  • 可复制std::shared_ptr 支持复制操作,每个复制的智能指针都会增加引用计数。
  • 线程安全:引用计数的增减操作是线程安全的(尽管整体的 std::shared_ptr 使用场景仍需注意线程安全)。
  • 定制删除器:同样支持自定义删除器。

3. std::weak_ptr

  • 弱引用std::weak_ptr 是一种弱引用,不能独立拥有对象,它必须从一个 std::shared_ptr 或另一个 std::weak_ptr 创建。
  • 不增加引用计数std::weak_ptr 不影响对象的生命周期,即不会增加引用计数,因此不会阻止对象被销毁。
  • 解决循环引用:主要用于解决 std::shared_ptr 间的循环引用问题,防止内存泄漏。
  • 访问对象:需要通过 lock() 方法获取一个 std::shared_ptr 来访问对象,如果对象已被销毁,lock() 将返回一个空的 std::shared_ptr

对比总结

  • 所有权
    • std::unique_ptr:独占所有权。
    • std::shared_ptr:共享所有权。
    • std::weak_ptr:弱引用,无所有权。
  • 生命周期管理
    • std::unique_ptr:超出作用域即销毁对象。
    • std::shared_ptr:基于引用计数,最后一个 shared_ptr 销毁时对象被销毁。
    • std::weak_ptr:不影响对象生命周期。
  • 复制行为
    • std::unique_ptr:不可复制,仅可移动。
    • std::shared_ptr:可复制,引用计数增加。
    • std::weak_ptr:可复制,不影响引用计数。
  • 用途
    • std::unique_ptr:适用于明确需要独占所有权的场景。
    • std::shared_ptr:适用于需要共享所有权的场景。
    • std::weak_ptr:适用于解决 shared_ptr 循环引用问题,或实现弱引用。

通过合理使用这三种智能指针,可以有效地管理动态内存,减少内存泄漏和悬挂指针的问题。

3.c++有几种智能指针?对比说明,使用表格 

在C++中,智能指针是一种用于自动管理动态分配内存的类模板。C++11标准引入了三种主要的智能指针:std::unique_ptrstd::shared_ptr 和 std::weak_ptr。此外,还有一种在C++98和C++03标准中存在的智能指针std::auto_ptr,但由于其设计缺陷已在C++11中被废弃,并在C++17中被完全移除。下面是关于这四种智能指针的对比说明,以表格形式呈现:

智能指针类型所有权复制性引用计数主要用途备注
std::auto_ptr独占所有权转移所有权不使用已被废弃在C++11中被弃用,C++17中被移除
std::unique_ptr独占所有权不可复制,仅可移动不使用管理单个对象的生命周期替代std::auto_ptr
std::shared_ptr共享所有权可复制使用允许多个指针共享同一对象通过引用计数管理生命周期
std::weak_ptr弱引用,无所有权可复制不使用解决std::shared_ptr循环引用问题不影响对象的引用计数

对比说明

  1. 所有权

    • std::auto_ptrstd::unique_ptr拥有独占所有权,这意味着在任何时候只有一个智能指针可以指向特定对象。
    • std::shared_ptr允许多个智能指针共享同一对象的所有权。
    • std::weak_ptr是弱引用,不拥有对象的所有权。
  2. 复制性

    • std::auto_ptr在复制时会转移所有权,导致源指针变为空。
    • std::unique_ptr不可复制,仅可通过移动语义转移所有权。
    • std::shared_ptr可复制,每次复制都会增加引用计数。
    • std::weak_ptr可复制,但复制不会影响对象的引用计数。
  3. 引用计数

    • std::auto_ptr不使用引用计数。
    • std::unique_ptr同样不使用引用计数。
    • std::shared_ptr使用引用计数来管理对象的生命周期,当引用计数变为0时,对象被销毁。
    • std::weak_ptr不使用引用计数,但可以与std::shared_ptr配合使用来检查对象是否仍然存在。
  4. 主要用途

    • std::auto_ptr(已被废弃)原本用于自动管理动态分配对象的生命周期。
    • std::unique_ptr适用于需要独占所有权的场景。
    • std::shared_ptr适用于需要共享所有权的场景。
    • std::weak_ptr主要用于解决std::shared_ptr之间的循环引用问题。
  5. 备注

    • std::auto_ptr由于设计缺陷已在C++11中被废弃,并在C++17中被完全移除。
    • std::unique_ptr是C++11中引入的智能指针,用于替代std::auto_ptr
    • std::shared_ptrstd::weak_ptr也是C++11中引入的智能指针,用于更复杂的内存管理场景。

通过上述表格和对比说明,可以清晰地了解每种智能指针的特点和用途。

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值