【面试题】【C++】请问,使用智能指针有没有内存泄漏的情况?

文章讲述了在C++中,当使用shared_ptr导致循环引用时会出现内存泄漏的问题。通过引入weak_ptr可以解决这个问题,因为weak_ptr不增加对象的引用计数,仅用于观察而不控制资源的生命周期。在循环引用的场景下,将shared_ptr替换为weak_ptr可以确保在没有活跃的shared_ptr引用时正确释放对象,从而避免内存泄漏。
摘要由CSDN通过智能技术生成
1. 请问,智能指针有没有内存泄漏的情况?

   有,当两个 shared_ptr 循环引用的时候,就会出现内存泄漏,此时需要引入 weak_ptr 来破局。

2. Circular reference problem using shared_ptr.

   Circular reference is a series of references such that each object i references the object i+1 and the last object references the first, thus forming a reference loop. To find out what’s wrong with a circular reference, let’s consider an example.

// forward declaration
struct Bar;

struct Foo {
  std::shared_ptr<Bar> pBar;
};

struct Bar {
  std::shared_ptr<Foo> pFoo;
};

   We have two structs referencing each other. This is an example of circular reference.

void someFunction() {
  // create struct objects
  std::shared_ptr<Foo> f = std::make_shared<Foo>();
  std::shared_ptr<Bar> b = std::make_shared<Bar>();

  // circular reference
  f->pBar = b;
  b->pFoo = f;

  // do something else
}

   When the function returns, the two objects created (one each of Foo and Bar) go out of scope. According to working of smart pointers, both objects should get automatically destroyed. But, this does not happen.
   When destructor for Bar runs, it checks whether there are any shared references to this object. Currently, there is one — reference pBar that belongs to Foo (b has already gone out of scope). So the Bar object initially co-owned by b will not be destructed. When destructor for Foo runs, the same happens. It checks for shared references and finds that pFoo that belongs to Bar owns the object. So the Foo object initially co-owned by f will not be destructed.
   When the program ends, there are two objects that never got destroyed. We have a memory leak!
   To sol this problem, we should use weak_ptr.

2. what is weak_ptr?

   A weak_ptr is a bit similar to a shared_ptr, in that, we can have multiple weak_ptrs owning a common object. It differs from shared_ptr by not taking part in the reference count. In other words, a weak_ptr does not increase reference count of the owned object. A weak_ptr must be used in conjunction with a shared_ptr. It’s used when we want to observe a resource, but do not want to control the life cycle of the resource.

3. How to solve the circular reference problem?

   Make use of weak_ptr to break the circular reference. So we change the shared_ptr in the previous example to weak_ptr.

// forward declaration
struct Bar;

struct Foo {
  std::weak_ptr<Bar> pBar;
};

struct Bar {
  std::weak_ptr<Foo> pFoo;
};

   The code for someFunction() stays the same.

void someFunction() {
  // create struct objects
  std::shared_ptr<Foo> f = std::make_shared<Foo>();
  std::shared_ptr<Bar> b = std::make_shared<Bar>();

  // no circular reference here
  f->pBar = b;
  b->pFoo = f;
  // the copy assignment of weak_ptr is getting called here.
  // It automatically *casts* shared_ptr to weak_ptr.

  // do something else
}

   Now, when the someFunction() function runs, we will not have a memory leak. Why? Because, when destroying the two objects that were created, there were no shared_ptr references to those objects. There were weak_ptr references; and weak_ptr references do not keep a resource alive. So both the objects get destroyed when their destructor runs.
   Using a weak_ptr we cannot access the owned object. There are no * and -> operators defined for weak_ptr. We have to get a shared_ptr to the owned object before using it. The lock() function of weak_ptr helps us do exactly that. It returns a shared_ptr object with the information preserved bby the weak_ptr. If the object has already been destroyed, lock() returns a shared_ptr object with default values.

void weakPtrDemo() {
  std::shared_ptr<int> sp1, sp2;
  std::weak_ptr<int> wp;

  sp1 = std::make_shared<int> (20);
  wp = sp1; // wp also owns the int

  // try getting shared_ptr from wp
  sp2 = wp.lock();
  sp1.reset(); // sp1 releases the ownership, but sp2 still owns the int

  sp1 = wp.lock(); // get the ownership back, now sp1 and sp2 co-own the int

  std::cout << "*sp1: " << *sp1 << std::endl; // 20
  std::cout << "*sp2: " << *sp2 << std::endl; // 20

  sp1.reset();
  sp2.reset();
  // the int will be freed as no shared pointer owns it.

  // lock() returns shared_ptr with default value
  // (nullptr in this case)
  sp1 = wp.lock();
  
  std::cout << "is sp1 valid: " << std::boolalpha
            << static_cast<bool> (sp1) << std::endl; // false
}
参考文献:

   https://www.codementor.io/@sandesh87/smart-pointers-in-c-part-5-1jdn6o4bcb

免责声明:

   本文素材来源网络,版权归原作者所有。如涉及作品版权问题,请与我联系删除。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值