c++ 智能指针 (std::weak_ptr)(三)

定义于头文件 <memory>

template< class T > class weak_ptr;    (C++11 起) 

std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性(「弱」)引用。在访问所引用的对象前必须先转换为 std::shared_ptr。

std::weak_ptr 用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr 被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。

std::weak_ptr 的另一用法是打断 std::shared_ptr 所管理的对象组成的环状引用。若这种环被孤立(例如无指向环中的外部共享指针),则 shared_ptr 引用计数无法抵达零,而内存被泄露。能令环中的指针之一为弱指针以避免此情况。

创建管理被引用的对象的shared_ptr

std::weak_ptr<T>::lock
std::shared_ptr<T> lock() const noexcept;   (C++11 起) 

创建新的 std::shared_ptr 对象,它共享被管理对象的所有权。若无被管理对象,即 *this 为空,则返回亦为空的 shared_ptr

等效地返回 expired() ? shared_ptr<T>() : shared_ptr<T>(*this) ,原子地执行。

参数

(无)

返回值

若 std::weak_ptr::expired 返回 false 则为共享被占有对象所有权的 shared_ptr 。否则返回默认构造的 T 类型的 shared_ptr

注意

此函数和 std::shared_ptr 的构造函数可能获得 std::weak_ptr 所指向的被管理对象的临时所有权。区别是 std::shared_ptr 的构造函数在其 std::weak_ptr 为空时抛异常,而 std::weak_ptr<T>::lock() 构造空的 std::shared_ptr<T> 。

调用示例

#include <iostream>
#include <memory>

void observe(std::weak_ptr<int> weak)
{
    if (auto observe = weak.lock())
    {
        std::cout << "\tobserve() able to lock weak_ptr<>, value=" << *observe << "\n";
    }
    else
    {
        std::cout << "\tobserve() unable to lock weak_ptr<>\n";
    }
}

int main()
{
    std::weak_ptr<int> weak;
    std::cout << "weak_ptr<> not yet initialized\n";
    observe(weak);

    {
        auto shared = std::make_shared<int>(42);
        weak = shared;
        std::cout << "weak_ptr<> initialized with shared_ptr.\n";
        observe(weak);
    }

    std::cout << "shared_ptr<> has been destructed due to scope exit.\n";
    observe(weak);
}

输出

提供弱指针的基于拥有者顺序

std::weak_ptr<T>::owner_before
template< class Y >
bool owner_before( const weak_ptr<Y>& other) const noexcept;
   
template< class Y >
bool owner_before( const std::shared_ptr<Y>& other) const noexcept; 

以实现定义的基于拥有者(与基于值相反)顺序,检查此 weak_ptr 是否先于 other 。二个智能指针仅若都占有同一对象或均为空才比较相等,即使由 get() 获得的指针不同(例如因为它们指向同一对象中的不同子对象)。

此顺序用于令共享和弱指针可用作关联容器中的关键,通常经由 std::owner_less 。

参数

other-要比较的 std::shared_ptr 或 std::weak_ptr

返回值

若 *this 前于 other 则为 true ,否则为 false 。常见实现比较控制块的地址。

调用示例

#include <iostream>
#include <memory>

struct Foo
{
    int n1;
    int n2;
    Foo(int a, int b) : n1(a), n2(b) {}
};
int main()
{
    auto p1 = std::make_shared<Foo>(1, 2);
    std::shared_ptr<int> p2(p1, &p1->n1);
    std::shared_ptr<int> p3(p1, &p1->n2);

    std::cout << std::boolalpha
              << "p2 < p3 " << (p2 < p3) << '\n'
              << "p3 < p2 " << (p3 < p2) << '\n'
              << "p2.owner_before(p3) " << p2.owner_before(p3) << '\n'
              << "p3.owner_before(p2) " << p3.owner_before(p2) << '\n';

    std::weak_ptr<int> w2(p2);
    std::weak_ptr<int> w3(p3);
    std::cout
            << "w2.owner_before(w3) " << w2.owner_before(w3) << '\n'
            << "w3.owner_before(w2) " << w3.owner_before(w2) << '\n';

    auto p5 = std::make_shared<int>(6);
    std::weak_ptr<int> w5(p5);
    std::cout
            << "w2.owner_before(w5) " << w2.owner_before(w5) << '\n'
            << "w5.owner_before(w2) " << w5.owner_before(w2) << '\n';
}

输出

为weak_ptr赋值

std::weak_ptr<T>::operator=

weak_ptr& operator=( const weak_ptr& r ) noexcept;

(1)(C++11 起)

template< class Y >
weak_ptr& operator=( const weak_ptr<Y>& r ) noexcept;

(2)(C++11 起)

template< class Y >
weak_ptr& operator=( const shared_ptr<Y>& r ) noexcept;

(3)(C++11 起)

weak_ptr& operator=( weak_ptr&& r ) noexcept;

(4)(C++14 起)

template< class Y >
weak_ptr& operator=( weak_ptr<Y>&& r ) noexcept;

(5)(C++14 起)

r 所管理者替换被管理对象。与 r 共享该对象。若 r 不管理对象,则 *this 亦不管理对象。

1-3) 等价于 std::weak_ptr<T>(r).swap(*this) 。

4,5) 等价于 std::weak_ptr<T>(std::move(r)).swap(*this) 。

参数

r-与之共享对象的智能指针

返回值

*this

注意

实现应满足要求而不创建临时的 weak_ptr 对象。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++智能指针 智能指针 智能指针概念 C/C++ 语⾔最为⼈所诟病的特性之⼀就是存在内存泄露问题,因此后来的⼤多数语⾔都提供了内置内存分配与释放功能,有的甚⾄⼲脆对语 ⾔的使⽤者屏蔽了内存指针这⼀概念。这⾥不置贬褒,⼿动分配内存与⼿动释放内存有利也有弊,⾃动分配内存和⾃动释放内存亦如此,这 是两种不同的设计哲学。有⼈认为,内存如此重要的东西怎么能放⼼交给⽤户去管理呢?⽽另外⼀些⼈则认为,内存如此重要的东西怎么能 放⼼交给系统去管理呢?在 C/C++ 语⾔中,内存泄露的问题⼀直困扰着⼴⼤的开发者,因此各类库和⼯具的⼀直在努⼒尝试各种⽅法去检 测和避免内存泄露,如 boost,智能指针技术应运⽽⽣。 智能指针主要⽤于管理在堆上分配的内存,它将普通的指针封装为⼀个栈对象。当栈对象的⽣存周期结束后,会在析构函数中释放掉申请的 内存,从⽽防⽌内存泄漏。简要的说,智能指针利⽤了 C++ 的 RAII 机制,在智能指针对象作⽤域结束后,会⾃动做内存释放的相关操作, 不需要我们再⼿动去操作内存。 RAII是C++的发明者Bjarne Stroustrup提出的概念,RAII全称是"Resource Acquisition is Initialization",直译过来是"资源获取即初始化",也 就是说在构造函数中申请分配资源,在析构函数中释放资源。因为C++的语⾔机制保证了,当⼀个对象创建的时候,⾃动调⽤构造函数,当 对象超出作⽤域的时候会⾃动调⽤析构函数。所以,在RAII的指导下,我们应该使⽤类来管理资源,将资源和对象的⽣命周期绑定。 C++ 中有四种智能指针:auto_pt、unique_ptr、shared_ptrweak_ptr 其中后个是 C++11 ⽀持,第⼀个已经被 C++11 弃⽤且被 unique_prt 代替,不推荐使⽤。下⽂将对其逐个说明。 std::auto_ptr 在这个年代讨论 std::auto_ptr 不免有点让⼈怀疑是不是有点过时了,确实如此,随着 C++11 标准的出现(最新标准是 C++20),std::auto_ptr 已经被彻底放弃,取⽽代之是 std::unique_ptr。然⽽,之所以还向介绍⼀下 std::auto_ptr 的⽤法以及它的设计不⾜ 之处是想更多了解 C++ 语⾔中智能指针的发展过程,⼀项技术如果我们了解它过去的样⼦和发展的轨迹,我们就能更好地掌握它。 std::auto_ptr 的基本⽤法如下代码所⽰: #include <iostream> #include <memory> using namespace std; int main() { //初始化⽅式1 std::auto_ptr<int> ap1(new int(8)); //初始化⽅式2 std::auto_ptr<int> ap2; ap2.reset(new int(8)); cout << *ap1 << ", " << *ap2 << endl; return 0; } 输出: 8, 8 智能指针对象 ap1 和 ap2 均持有⼀个在堆上分配 int 对象,其值均是 8,这两块堆内存均可以在 ap1 和 ap2 释放时得到释放。这是 std::auto_ptr 的基本⽤法。 std::auto_ptr 真正让⼈容易误⽤的地⽅是其不常⽤的复制语义,即当复制⼀个 std::auto_ptr 对象时(拷贝复制或 operator= 复制),原对象 所持有的堆内存对象也会转移给复制出来的对象。⽰例代码如下: #include <memory> int main() { //测试拷贝构造 std::auto_ptr<int> ap1(new int(8)); std::auto_ptr<int> ap2(ap1); if (ap1.get() != NULL) { std::cout << "ap1 is not empty." << std::endl; } else { std::cout << "ap1 is empty." << std::endl; } if (ap2.get() != NULL) { std::cout << "ap2 is not empty." << std::endl; } else { std::cout << "ap2 is empty." << std::endl; } //测试赋值构造 std::auto_ptr<int> ap3(new int(8)); std::auto_ptr<int> ap4; ap4 = ap3; if (ap3.get() != NULL) { std::cout << "ap3 is not empty." << std::endl; }
C++智能指针详解 1、概述 我们知道除了静态内存和栈内存外,每个程序还有⼀个内存池,这部分内存被称为⾃由空间或者堆。程序⽤堆来存储动态分配的对象即那些 在程序运⾏时分配的对象,当动态对象不再使⽤时,我们的代码必须显式的销毁它们。 在C++中,动态内存的管理是⽤⼀对运算符完成的:new和delete。 new:在动态内存中为对象分配⼀块空间并返回⼀个指向该对象的指针; delete:指向⼀个动态独享的指针,销毁对象,并释放与之关联的内存。 动态内存管理经常会出现两种问题: (1)⼀种是忘记释放内存,会造成内存泄漏; (2)⼀种是尚有指针引⽤内存的情况下就释放了它,就会产⽣引⽤⾮法内存的指针。 为了更加容易(更加安全)的使⽤动态内存,引⼊了智能指针的概念。智能指针的⾏为类似常规指针,重要的区别是它负责⾃动释放所指向 的对象。 标准库提供的两种智能指针的区别在于管理底层指针的⽅法不同:shared_ptr和unique_ptr。 (1)shared_ptr允许多个指针指向同⼀个对象; (2)unique_ptr则"独占"所指向的对象。 标准库还定义了⼀种名为weak_ptr的伴随类,它是⼀种弱引⽤,指向shared_ptr所管理的对象,这智能指针都定义在memory头⽂件 中。 2、auto_ptr (不要使⽤的指针) (1)内部⼤概实现:做成⼀个auto_ptr类,包含原始指针成员。 当auto_ptr类型的对象被释放时,利⽤析构函数,将拥有的原始指针delete掉。 //⼤概长这个样⼦(简化版) template<class T> class auto_ptr {   T* ptr; }; (2)⽰例⽤法: void runGame() { std::auto_ptr<Monster> monster1(new Monster());//monster1 指向 ⼀个怪物 monster1->doSomething();//怪物做某种事 }//runGame函数执⾏完时,monster1被释放,然后它的析构函数也把指向的⼀个怪物释放了 复制auto_ptr对象时,把指针指传给复制出来的对象,原有对象的指针成员随后重置为nullptr。 这说明auto_ptr是独占性的,不允许多个auto_ptr指向同⼀个资源。 void runGame() { std::auto_ptr<Monster> monster1(new Monster());//monster1 指向 ⼀个怪物 monster1->doSomething(); //怪物做某种事 std::auto_ptr<Monster> monster2 = monster1; //转移指针 monster2->doSomething(); //怪物做某种事 monster1->doSomething(); //Oops!monster1智能指针指向了nullptr,运⾏期崩溃。 } 注意: 虽然本⽂简单介绍了auto_ptr。 但是不要⽤auto_ptr! 不要⽤auto_ptr! 虽然它是c++11以前的最原始的智能指针,但是在c++11中已经被弃⽤(使⽤的话会被警告)了。 它的替代品,也就是c++11新智能指针unique_ptr,shared_ptr,weak_ptr。 3、shared_ptr(⼀种强引⽤指针) 多个shared_ptr指向同⼀处资源,当所有shared_ptr都全部释放时,该处资源才释放。 (有某个对象的所有权(访问权,⽣命控制权) 即是 强引⽤,所以shared_ptr是⼀种强引⽤型指针) (1)内部⼤概实现:每次复制,多⼀个共享同处资源的shared_ptr时,计数+1。每次释放shared_ptr时,计数-1。 当shared计数为0时,则证明所有指向同⼀处资源的shared_ptr们全都释放了,则随即释放该资源(哦,还会释放new出来的 SharedPtrControlBlock)。 //shared计数放在这个结构体⾥⾯,实际上结构体⾥还应该有另⼀个weak计数。下⽂介绍weak_ptr时会解释。 struct SharedPtrControlBlock {   int shared_count; }; //⼤概长这个样⼦(简化版) template<class T> class shared_ptr {   T* ptr;   SharedPtrControlBlock* count; }; (2)⽰例⽤法: void runGame() {   std::shared_ptr<Monster> monster1(new Monster());  //计数加到1  do{ std::shared_ptr<Monster> monster
智能指针 智能指针shared_ptr的⽤法 的⽤法   为了解决C++内存泄漏的问题,C++11引⼊了智能指针(Smart Pointer)。   智能指针的原理是,接受⼀个申请好的内存地址,构造⼀个保存在栈上的智能指针对象,当程序退出栈的作⽤域范围后,由于栈上的变 量⾃动被销毁,智能指针内部保存的内存也就被释放掉了(除⾮将智能指针保存起来)。   C++11提供了智能指针std::shared_ptr, std::unique_ptr, std::weak_ptr,使⽤时需添加头⽂件<memory>。   shared_ptr使⽤引⽤计数,每⼀个shared_ptr的拷贝都指向相同的内存。每使⽤他⼀次,内部的引⽤计数加1,每析构⼀次,内部的引⽤ 计数减1,减为0时,删除所指向的堆内存。shared_ptr内部的引⽤计数是安全的,但是对象的读取需要加锁。 1. shared_ptr的基本⽤法 初始化   可以通过构造函数、std::make_shared<T>辅助函数和reset⽅法来初始化shared_ptr: #include "stdafx.h" #include <iostream> #include <future> #include <thread> using namespace std; class Person { public: Person(int v) { value = v; std::cout << "Cons" <<value<< std::endl; } ~Person() { std::cout << "Des" <<value<< std::endl; } int value; }; int main() { std::shared_ptr<Person> p1(new Person(1));// Person(1)的引⽤计数为1 std::shared_ptr<Person> p2 = std::make_shared<Person>(2); p1.reset(new Person(3));// ⾸先⽣成新对象,然后引⽤计数减1,引⽤计数为0,故析构Person(1) // 最后将新对象的指针交给智能指针 std::shared_ptr<Person> p3 = p1;//现在p1和p3同时指向Person(3),Person(3)的引⽤计数为2 p1.reset();//Person(3)的引⽤计数为1 p3.reset();//Person(3)的引⽤计数为0,析构Person(3) return 0; }   注意,不能将⼀个原始指针直接赋值给⼀个智能指针,如下所⽰,原因是⼀个是类,⼀个是指针。 std::shared_ptr<int> p4 = new int(1);// error   reset()包含两个操作。当智能指针中有值的时候,调⽤reset()会使引⽤计数减1.当调⽤reset(new xxx())重新赋值时,智能指针⾸先是⽣ 成新对象,然后将就对象的引⽤计数减1(当然,如果发现引⽤计数为0时,则析构旧对象),然后将新对象的指针交给智能指针保管。 获取原始指针   std::shared_ptr<int> p4(new int(5)); int *pInt = p4.get(); 指定删除器   智能指针可以指定删除器,当智能指针的引⽤计数为0时,⾃动调⽤指定的删除器来释放内存。std::shared_ptr可以指定删除器的⼀个原 因是其默认删除器不⽀持数组对象,这⼀点需要注意。   2. 使⽤shared_ptr需要注意的问题   但凡⼀些⾼级的⽤法,使⽤时都有不少陷阱。 不要⽤⼀个原始指针初始化多个shared_ptr,原因在于,会造成⼆次销毁,如下所⽰: int *p5 = new int; std::shared_ptr<int> p6(p5); std::shared_ptr<int> p7(p5);// logic error 不要在函数实参中创建shared_ptr。因为C++的函数参数的计算顺序在不同的编译器下是不同的。正确的做法是先创建好,然后再传 ⼊。 function(shared_ptr<int>(new int), g()); 禁⽌通过shared_from_this()返回this指针,这样做可能也会造成⼆次析构。 避免循环引⽤。智能指针最⼤的⼀个陷阱是循环引⽤,循环引⽤会导致内存泄漏。解决⽅法是AStruct或BStruct改为weak_ptr。 struct AStruct; struct BStruct; struct AStruct { std::shared_ptr<BStruct> bPtr; ~AStruct() {
C++智能指针详解 智能指针详解 智能指针内容很多,重点是基本⽤法。 #include <boost/shared_ptr.hpp> class CBase: public boost::enable_shared_from_this<CBase> { public: virtual void f(){}//必须有个虚函数才能向上向下转换。 } typedef boost::shared_ptr<CBase> CBasePtr; class CChild: public CBase {} typedef boost::shared_ptr<CChild> CChildPtr; void main() { CBasePtr ptrBase = boost::make_shared<CBase>(); //CBasePtr ptrBase = CBasePtr(new CBase()); // 向下转换 CChildPtr ptrChild = boost::dynamic_pointer_cast<CChild>(ptrBase); // 向上转换 CBasePtr ptrXXX = ptrChild; // 普通转换 CChildPtr ptrXX = CChildPtr(dynamic_cast<CChild*>(ptrXXX.get())); } 暂时学会这些⽤法即可。 url: C++ 智能指针详解 ⼀、简介 由于 C++ 语⾔没有⾃动内存回收机制,程序员每次 new 出来的内存都要⼿动 delete。程序员忘记 delete,流程太复杂,最终导致没 有 delete,异常导致程序过早退出,没有执⾏ delete 的情况并不罕见。 ⽤智能指针便可以有效缓解这类问题,本⽂主要讲解参见的智能指针的⽤法。包 括:std::auto_ptrboost::scoped_ptr、boost::shared_ptr、boost::scoped_array、、boost::weak_ptr、boost::intrusive_ptr。你可能会想,如 此多的智能指针就为了解决new、delete匹配问题,真的有必要吗?看完这篇⽂章后,我想你⼼⾥⾃然会有答案。 下⾯就按照顺序讲解如上 7 种智能指针(smart_ptr)。 ⼆、具体使⽤ 1、总括 对于编译器来说,智能指针实际上是⼀个栈对象,并⾮指针类型,在栈对象⽣命期即将结束时,智能指针通过析构函数释放有它管理的 堆内存。所有智能指针都重载了"operator->"操作符,直接返回对象的引⽤,⽤以操作对象。访问智能指针原来的⽅法则使⽤"."操作符。 访问智能指针包含的裸指针则可以⽤ get() 函数。由于智能指针是⼀个对象,所以if (my_smart_object)永远为真,要判断智能指针的裸指 针是否为空,需要这样判断:if (my_smart_object.get())。 智能指针包含了 reset() ⽅法,如果不传递参数(或者传递 NULL),则智能指针会释放当前管理的内存。如果传递⼀个对象,则智能指 针会释放当前对象,来管理新传⼊的对象。 我们编写⼀个测试类来辅助分析: class Simple { public: Simple(int param = 0) { number = param; std::cout << "Simple: " << number << std::endl; } ~Simple() { std::cout << "~Simple: " << number << std::endl; } void PrintSomething() { std::cout << "PrintSomething: " << info_extend.c_str() << std::endl; } std::string info_extend; int number; }; 2、std::auto_ptr std::auto_ptr 属于 STL,当然在 namespace std 中,包含头⽂件 #include<memory> 便可以使⽤。std::auto_ptr 能够⽅便的管理单个堆 内存对象。 我们从代码开始分析: void TestAutoPtr() { std::auto_ptr<Simple> my_memory(new Simple(1)); // 创建对象,输出:Simple:1 if (my_memory.get()) { // 判断智能指针是否为空 my_memory->PrintSomething(); // 使⽤ operator-> 调⽤智能指针对象中的函数 my_memory.get()->info_extend = "Addition";

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值