C++11 std::弱ptr何时有用?

C++11 std::弱ptr何时有用?,c++11,shared-ptr,weak-ptr,c++-faq,C++11,Shared Ptr,Weak Ptr,C++ Faq,我开始研究C++11的智能指针,但我看不到std::weak_ptr有任何有用的用途。有人能告诉我什么时候std::weak_ptr有用/必要吗?缓存就是一个很好的例子 对于最近访问的对象,您希望将它们保留在内存中,因此可以保持指向它们的强指针。定期扫描缓存并确定哪些对象最近未被访问。您不需要将它们保存在内存中,因此可以去掉强指针 但是,如果该对象正在使用,而其他一些代码持有指向它的强指针,该怎么办?如果缓存删除了指向该对象的唯一指针,它将永远无法再次找到它。因此,缓存会保留一个弱指针,指向它需

我开始研究C++11的智能指针,但我看不到
std::weak_ptr
有任何有用的用途。有人能告诉我什么时候
std::weak_ptr
有用/必要吗?

缓存就是一个很好的例子

对于最近访问的对象,您希望将它们保留在内存中,因此可以保持指向它们的强指针。定期扫描缓存并确定哪些对象最近未被访问。您不需要将它们保存在内存中,因此可以去掉强指针

但是,如果该对象正在使用,而其他一些代码持有指向它的强指针,该怎么办?如果缓存删除了指向该对象的唯一指针,它将永远无法再次找到它。因此,缓存会保留一个弱指针,指向它需要查找的对象(如果它们恰好留在内存中)


这正是弱指针所做的——它允许您在对象仍然存在的情况下定位对象,但如果没有其他东西需要它,它就不会保留对象。

以下是@jleahy给我的一个示例:假设您有一组任务,异步执行,并由
std::shared\u ptr
管理。您可能希望定期对这些任务执行某些操作,因此计时器事件可能会遍历
std::vector
,并为任务提供一些操作。然而,同时,一个任务可能已经同时决定不再需要它并死亡。因此,计时器可以通过从弱指针生成共享指针并使用该共享指针来检查任务是否仍处于活动状态,前提是该指针不为空。

另一个答案,希望更简单。(适用于谷歌同行)

假设您有
团队
成员
对象

显然这是一种关系:
团队
对象将有指向其
成员的指针。而且,成员也可能有一个指向他们的
团队
对象的反向指针

然后你有一个依赖循环。如果使用
shared\u ptr
,当您放弃对对象的引用时,对象将不再自动释放,因为它们以循环方式相互引用。这是内存泄漏

您可以使用
弱\u ptr
来打破这一点。“所有者”通常使用
shared\u ptr
,“所有者”使用
弱\u ptr
,并在需要访问其父级时将其临时转换为
shared\u ptr

存储弱ptr:

weak_ptr<Parent> parentWeakPtr_ = parentSharedPtr; // automatic conversion to weak from shared
弱\u ptr parentWeakPtr\uu=parentSharedPtr;//从共享到弱的自动转换
然后在需要时使用它

shared_ptr<Parent> tempParentSharedPtr = parentWeakPtr_.lock(); // on the stack, from the weak ptr
if( !tempParentSharedPtr ) {
  // yes, it may fail if the parent was freed since we stored weak_ptr
} else {
  // do stuff
}
// tempParentSharedPtr is released when it goes out of scope
shared_ptr tempParentSharedPtr=parentWeakPtr_ukptr.lock();//在堆栈上,从弱ptr
如果(!tempParentSharedPtr){
//是的,如果父级被释放,它可能会失败,因为我们存储了弱\u ptr
}否则{
//做事
}
//tempParentSharedPtr超出范围时将被释放

std::weak_ptr
是解决此问题的一个非常好的方法。仅仅使用原始指针不可能知道引用的数据是否已被释放。相反,通过让
std::shared_ptr
管理数据,并向数据用户提供
std::弱_ptr
,用户可以通过调用
expired()
lock()
来检查数据的有效性

单独使用
std::shared_ptr
无法做到这一点,因为所有
std::shared_ptr
实例共享数据的所有权,而这些数据在
std::shared_ptr
的所有实例都被删除之前不会被删除。下面是一个如何使用
lock()
检查悬空指针的示例:

#包括
#包括
int main()
{
//指针悬空的老问题
//问题:ref将指向未定义的数据!
int*ptr=新的int(10);
int*ref=ptr;
删除ptr;
//新的
//解决方案:检查expired()或lock()以确定指针是否有效
//空定义
std::共享的ptr sptr;
//取得指针的所有权
sptr.重置(新int);
*sptr=10;
//获取指向数据的指针而不获取所有权
std::弱ptr weak1=sptr;
//删除托管对象,获取新指针
sptr.重置(新int);
*sptr=5;
//获取指向新数据的指针而不获取所有权
std::弱ptr weak2=sptr;
//weak1过期了!
if(auto tmp=weak1.lock())
标准::cout
std::weak_ptr是一个智能指针,它持有对std::shared_ptr管理的对象的非所有权(“弱”)引用。必须将其转换为std::shared_ptr才能访问被引用的对象

std::weak_ptr为临时所有权建模:当一个对象只有在存在时才需要访问,并且可能被其他人随时删除时,std::weak_ptr用于跟踪该对象,并将其转换为std::shared_ptr以承担临时所有权。如果原始std::shared_ptr此时被销毁,则该对象的生存期为e直到临时std::shared_ptr也被销毁


此外,std::weak_ptr用于中断std::shared_ptr的循环引用。

weak_ptr
还可以检查对象的正确删除,尤其是在单元测试中。典型用例可能如下所示:

std::weak_ptr<X> weak_x{ shared_x };
shared_x.reset();
BOOST_CHECK(weak_x.lock());
... //do something that should remove all other copies of shared_x and hence destroy x
BOOST_CHECK(!weak_x.lock());
std::弱_ptr弱_x{shared_x};
共享_x.reset();
增强检查(弱锁定());
…//执行一些操作,删除共享_x的所有其他副本,从而销毁x
增强检查(!弱锁定());

当调用异步处理程序时无法保证目标对象仍然存在时,它们对Boost.Asio非常有用。诀窍是使用
std::bind
或lambda捕获将
弱\u ptr
绑定到异步处理程序对象中

void MyClass::startTimer()
{
    std::weak_ptr<MyClass> weak = shared_from_this();
    timer_.async_wait( [weak](const boost::system::error_code& ec)
    {
        auto self = weak.lock();
        if (self)
        {
            self->handleTimeout();
        }
        else
        {
            std::cout << "Target object no longer exists!\n";
        }
    } );
}
void MyClass::startTimer()
{
std::weak_ptr-weak=来自_this()的共享_;
计时器异步等待([弱](常量boost::system::error\u code&ec)
{
auto self=weak.lock();
如果(自我)
{
self->handleTimeout();
}
其他的
{
std::coutsha有一个缺点
void MyClass::startTimer()
{
    std::weak_ptr<MyClass> weak = shared_from_this();
    timer_.async_wait( [weak](const boost::system::error_code& ec)
    {
        auto self = weak.lock();
        if (self)
        {
            self->handleTimeout();
        }
        else
        {
            std::cout << "Target object no longer exists!\n";
        }
    } );
}
class A
{
    shared_ptr<int> sPtr1;
    weak_ptr<int> wPtr1;
}
shard_ptr<A> <----| shared_ptr<B> <------
    ^             |          ^          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
class A           |     class B         |
    |             |          |          |
    |             ------------          |
    |                                   |
    -------------------------------------
class B;

class A
{
    shared_ptr<B> sP1; // use weak_ptr instead to avoid CD

public:
    A() {  cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }

    void setShared(shared_ptr<B>& p)
    {
        sP1 = p;
    }
};

class B
{
    shared_ptr<A> sP1;

public:
    B() {  cout << "B()" << endl; }
    ~B() { cout << "~B()" << endl; }

    void setShared(shared_ptr<A>& p)
    {
        sP1 = p;
    }
};

int main()
{
    shared_ptr<A> aPtr(new A);
    shared_ptr<B> bPtr(new B);

    aPtr->setShared(bPtr);
    bPtr->setShared(aPtr);

    return 0;  
}
A()
B()
// a simplified class to hold the thumbnail and data
struct ImageData {
  std::string path;
  std::unique_ptr<YourFavoriteImageLibData> image;
};

// a simplified reader fn
void read( std::vector<std::shared_ptr<ImageData>> imagesToLoad ) {
   for( auto& imageData : imagesToLoad )
     imageData->image = YourFavoriteImageLib::load( imageData->path );
}

// a simplified manager
class Manager {
   std::vector<std::shared_ptr<ImageData>> m_imageDatas;
   std::vector<std::unique_ptr<std::thread>> m_threads;
public:
   void load( const std::string& folderPath ) {
      std::vector<std::string> imagePaths = readFolder( folderPath );
      m_imageDatas = createImageDatas( imagePaths );
      const unsigned numThreads = std::thread::hardware_concurrency();
      std::vector<std::vector<std::shared_ptr<ImageData>>> splitDatas = 
        splitImageDatas( m_imageDatas, numThreads );
      for( auto& dataRangeToLoad : splitDatas )
        m_threads.push_back( std::make_unique<std::thread>(read, dataRangeToLoad) );
   }
};
// a simplified reader fn
void read( std::vector<std::weak_ptr<ImageData>> imagesToLoad ) {
   for( auto& imageDataWeak : imagesToLoad ) {
     std::shared_ptr<ImageData> imageData = imageDataWeak.lock();
     if( !imageData )
        continue;
     imageData->image = YourFavoriteImageLib::load( imageData->path );
   }
}

// a simplified manager
class Manager {
   std::vector<std::shared_ptr<ImageData>> m_imageDatas;
   std::vector<std::unique_ptr<std::thread>> m_threads;
public:
   void load( const std::string& folderPath ) {
      std::vector<std::string> imagePaths = readFolder( folderPath );
      m_imageDatas = createImageDatas( imagePaths );
      const unsigned numThreads = std::thread::hardware_concurrency();
      std::vector<std::vector<std::weak_ptr<ImageData>>> splitDatas = 
        splitImageDatasToWeak( m_imageDatas, numThreads );
      for( auto& dataRangeToLoad : splitDatas )
        m_threads.push_back( std::make_unique<std::thread>(read, dataRangeToLoad) );
   }
};
// Some sort of image; very expensive to create.
std::shared_ptr< Texture > texture;

// A Widget should be able to quickly get a handle to a Texture. On the
// other hand, I don't want to keep Textures around just because a widget
// may need it.

struct Widget {
    std::weak_ptr< Texture > texture_handle;
    void render() {
        if (auto texture = texture_handle.get(); texture) {
            // do stuff with texture. Warning: `texture`
            // is now extending the lifetime because it
            // is a std::shared_ptr< Texture >.
        } else {
            // gracefully degrade; there's no texture.
        }
    }
};
// Asking for trouble because a node owns the next node, and the next node owns
// the previous node: memory leak; no destructors automatically called.
struct Node {
    std::shared_ptr< Node > next;
    std::shared_ptr< Node > prev;
};

// Asking for trouble because a parent owns its children and children own their
// parents: memory leak; no destructors automatically called.
struct Node {
    std::shared_ptr< Node > parent;
    std::shared_ptr< Node > left_child;
    std::shared_ptr< Node > right_child;
};

// Better: break dependencies using a std::weak_ptr (but not best way to do it;
// see Herb Sutter's talk).
struct Node {
    std::shared_ptr< Node > next;
    std::weak_ptr< Node > prev;
};

// Better: break dependencies using a std::weak_ptr (but not best way to do it;
// see Herb Sutter's talk).
struct Node {
    std::weak_ptr< Node > parent;
    std::shared_ptr< Node > left_child;
    std::shared_ptr< Node > right_child;
};
#include<iostream>
#include<memory>
using namespace std;

class B;

class A
{
public:
    shared_ptr<B>bptr;
    A() {
        cout << "A created" << endl;
    }
    ~A() {
        cout << "A destroyed" << endl;
    }
};

class B
{
public:
    shared_ptr<A>aptr;
    B() {
        cout << "B created" << endl;
    }
    ~B() {
        cout << "B destroyed" << endl;
    }
};

int main()
{
    {
        shared_ptr<A> a = make_shared<A>();
        shared_ptr<B> b = make_shared<B>();
        a->bptr = b;
        b->aptr = a;
    }
  // put breakpoint here
}
A created
B created
class B;
class A
{
public:
    weak_ptr<B>bptr;

    A() {
        cout << "A created" << endl;
    }
    ~A() {
        cout << "A destroyed" << endl;
    }
};

class B
{
public:
    weak_ptr<A>aptr;

    B() {
        cout << "B created" << endl;
    }
    ~B() {
        cout << "B destroyed" << endl;
    }
};

    int main()
    {
        {
            shared_ptr<A> a = make_shared<A>();
            shared_ptr<B> b = make_shared<B>();
            a->bptr = b;
            b->aptr = a;
        }
      // put breakpoint here
    }
A created
B created
B destroyed
A destroyed