智能指针与堆内存管理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/deeplan_1994/article/details/83307985

目录

shared_ptr

std::weak_ptr

std::unique_ptr

使用时注意事项:

①.new的普通指针与shared_ptr转换:

②.指向栈的指针与shared_ptr转换:

3.智能指针向常规指针的转换


自从c++11引入智能指针shared_ptr后,我们似乎再也不用担心new的内存没有释放之类的问题了,但是也带来了其他的问题。

shared_ptr

智能指针是指向动态分配(堆)对象的指针,用于生存期控制,能确保自动正确的销毁动态分配的对象,防止内存泄露。它的一种通用实现技术是使用引用计数。每次使用它,内部引用计数加1,每析构一次,内部引用计数减1,减为0时,删去所指向的堆内存。

C++11中的智能指针包括:

std::shared_ptr

std::unique_ptr

std::weak_ptr

其中最常用的就是std::shared_ptr。每个shared_ptr的拷贝都指向相同的内存.最后一个shared_ptr析构的时候,堆中的内存才会被释放。share_ptr类型的对象具有获得指针所有权并共享所有权的能力。

初始化:

可以通过指针来初始化它:template <class U> explicit shared_ptr (U *p);

std::shared_ptr<int>p(new int(2));

std::shared_ptr<int>p2= p;

std::shared_ptr<BaseConnector> m_connt = make_shared<Connector>(m_ios, m_strIP, m_port);

通过构造函数、赋值函数或者 make_shared 函数初始化智能指针。

例子:

// shared_ptr constructor example
#include <iostream>
#include <memory>

struct C {int* data;};

int main () {
  std::shared_ptr<int> p1;
  std::shared_ptr<int> p2 (nullptr);
  std::shared_ptr<int> p3 (new int);
  std::shared_ptr<int> p4 (new int, std::default_delete<int>());
  std::shared_ptr<int> p5 (new int, [](int* p){delete p;}, std::allocator<int>());
  std::shared_ptr<int> p6 (p5);
  std::shared_ptr<int> p7 (std::move(p6));
  std::shared_ptr<int> p8 (std::unique_ptr<int>(new int));
  std::shared_ptr<C> obj (new C);
  std::shared_ptr<int> p9 (obj, obj->data);

  std::cout << "use_count:\n";
  std::cout << "p1: " << p1.use_count() << '\n';
  std::cout << "p2: " << p2.use_count() << '\n';
  std::cout << "p3: " << p3.use_count() << '\n';
  std::cout << "p4: " << p4.use_count() << '\n';
  std::cout << "p5: " << p5.use_count() << '\n';
  std::cout << "p6: " << p6.use_count() << '\n';
  std::cout << "p7: " << p7.use_count() << '\n';
  std::cout << "p8: " << p8.use_count() << '\n';
  std::cout << "p9: " << p9.use_count() << '\n';
  return 0;
}

2. 智能指针中的原始指针,通过 get 获取
char* pData = pBuf.get();

3.注意事项。智能指针虽然能自动管理堆内存,但他有不少陷进,使用时要注意:

     1.不要把一个原生指针给多个shared_ptr管理

     

int* ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); //logic error
导致ptr被删除两次

       2.不要把this指针赋给shared_ptr

       3. 不要在函数实参里创建shared_ptr

         function( share_ptr<int>(new int), g( ) );//有缺陷

        可能的过程是先new int,然后调g( ),g( )发生异常,shared_ptr<int>没有创建,int内存泄露

        shared_ptr<int> p(new int());
        f(p, g()); 

5.shared_ptr作为对象的成员时,小心因循环引用造成无法释放资源。

struct A;
struct B;
struct A
{
std::shared_ptr<B> m_b;
};

struct B
{
std::shared_ptr<A> m_a;
};

std::shared_ptr<A> ptrA(new A);
std::shared_ptr<B> ptrB(new B);
ptrA->m_b = ptrB;
ptrB->m_a = ptrA;

ptrA和ptrB相互引用,离开作用域时引用计数都为1,导致内存没有被释放,解决办法是把A和B任何一个的成员变量改为weak_ptr
解决办法是把A和B任何一个的成员变量改为weak_ptr

struct B
{
std::weak_ptr<A> m_a;
};

ptrB->m_a不会增加A对象的引用计数,因此A对象离开作用域时,引用计数为0,m_b的引用计数减一,b离开作用域后引用计数由1减为0.

std::weak_ptr

弱引用指针,用来监视智能指针,不会使应用计数加1.在访问所引用对象前必须先转换成std::shared.

用来管理临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时都可能被他人删去,可以使用。

来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的std::shared_ptr 被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。

std::unique_ptr

unique_ptr不会共享它的指针。 无法将它复制到另一个unique_ptr, unique_ptr只能移动。 这意味着内存资源的所有权将转移到新的unique_ptr和原始unique_ptr不再拥有它。
int* p = new int;
std::unique_ptr<int> ptr(p);
std::unique_ptr<int> ptr1 = ptr; //不能复制,编译报错

auto ptr2 = std::move(ptr); //转移所有权, 现在ptr那块内存归ptr2所有, ptr成为无效的指针.

使用时注意事项:

1.常规指针转换为智能指针:

①.new的普通指针与shared_ptr转换:

如图所示,这会发生什么情况?答案是输出的会是随机数,因为经过func函数后,我们用p初始化的临时智能指针已经被析构了,引用计数先+1,后-1。所以经过func函数后,

p指向的对象被释放,再解引用自然无法得到我们想要的结果。

#include<iostream>
#include <memory>
using namespace std;
void func(shared_ptr<int>)
{
    ;
}
int main()
{
    int a = 5;
    auto p = new int(5);
    func(shared_ptr<int>(p));
    cout << *p << endl;
    return 0;
}

这种情况下,正确的做法如图所示:一开始就使用智能指针。

#include<iostream>
#include <memory>
using namespace std;
void func(shared_ptr<int>)
{
    ;
}
int main()
{
    //int a = 5;
    auto p = make_shared<int>(5);
    func(shared_ptr<int>(p));
    cout << *p << endl;
    return 0;
}

②.指向栈的指针与shared_ptr转换:

如图所示,这种情况,程序会直接崩溃,因为智能指针试图释放保存在栈上的变量,它越界了

#include<iostream>
#include <memory>
using namespace std;
void func(shared_ptr<int>)
{
    ;
}
int main()
{
    int a = 5;
    auto p = &a;
    func(shared_ptr<int>(p));
    cout << *p << endl;
    return 0;
}

3.智能指针向常规指针的转换

我们通常使用get()函数向智能指针索要所指向对象的拥有权,但是这样有时也会造成错误:

auto p = make_shared<int>(42);
int* iPtr = p.get();
{
   shared_ptr<int>(iPtr);
}

int value = *p; // Error! 内存已经被释放

p与iPtr指向了相同的内存,然而通过get方法后,将内存管理权转移给了普通指针。iPtr传递给里面程序块的临时智能指针后,引用计数为1,随后出了作用域,减少为0,释放内存。

猜你喜欢

转载自blog.csdn.net/deeplan_1994/article/details/83307985
今日推荐