0
点赞
收藏
分享

微信扫一扫

shared_ptr注意事项

这是一篇关于讲解shared_ptr使用过程中常见的问题及对应方法的文章

什么是shared_ptr

自行百度。

常见问题

1.多次引用同一数据

{
int* p = new int[10];
std::shared_ptr<int> sp1(p);
std::shared_ptr<int> sp2(p);
}

它会导致两次释放同一块内存,而破坏堆。

2.使用shared_ptr包装this指针带来的问题

class A
{
public:
~A(){std::cout<<"~dtor\n"}
std::shared_ptr<A> myGet()
{
  return std::shared_ptr<A>(this);
}
};
int main()
{
 A a;
 std::shared_ptr<A> sp=a.myGet();
return 0;
}

将导致两次释放t对象破坏堆栈,一次是出栈时析构,一次就是shared_ptr析构. 可用以下方法破解:

class A:public enable_shared_from_this<A>
{
~A(){std::cout<<"~dtor A\n"}
std::shared_ptr<A> myGet()
{
  return shared_from_this();
}
};
int main()
{
    {
        std::shared_ptr<A> sp1(new A);
        sp1->myGet();
        cout << "cout:" << sp1.use_count() << endl;
    }
    getchar();
    return 0;
}

3..循环引用

class parent;
class child; 
typedef boost::shared_ptr<parent> parent_ptr;
typedef boost::shared_ptr<child> child_ptr; 
class parent
{
public:
       ~parent() { 
              std::cout <<"父类析构函数被调用.\n"; 
       }
 
public:
       child_ptr children;
};
 
class child
{
public:
       ~child() { 
              std::cout <<"子类析构函数被调用.\n"; 
       }
 
public:
       parent_ptr parent;
};
 
int main()
{
     parent_ptr father(new parent());
       child_ptr son(new child);
       // 父子互相引用。
       father->children = son;
       son->parent = father;
    return 0;
}

在程序退出前,father的引用计数为2,son的计数也为2,退出时,shared_ptr所作操作就是简单的将计数减1,如果为0则释放,显然,这个情况下,引用计数不为0,于是造成father和son所指向的内存得不到释放,导致内存泄露。

4.多线程问题

class tester 
{
public:
  tester() {}
  ~tester() {}
// 更多的函数定义…
};

void fun(boost::shared_ptr<tester> sp)
{
   // !!!在这大量使用sp指针.
 boost::shared_ptr<tester> tmp = sp;
}

int main()
{
   boost::shared_ptr<tester> sp1(new tester);
   // 开启两个线程,并将智能指针传入使用。
                   boost::thread t1(boost::bind(&fun, sp1));
                      boost::thread t2(boost::bind(&fun, sp1));

                      t1.join();
     t2.join();
   return 0;
}
    

由于多线程同是访问智能指针,并将其赋值到其它同类智能指针时,很可能发生两个线程同时在操作引用计数(但并不一定绝对发生),而导致计数失败或无效等情况,从而导致程序崩溃. 要解决这类问题的办法也很简单,使用weak_ptr就可以很方便解决这个问题。

class tester 
{
public:
   tester() {}
   ~tester() {}
// 更多的函数定义…
};
 
void fun(boost::weak_ptr<tester> wp)
{
  boost::shared_ptr<tester> sp = wp.lock;
  if (sp)
{
     // 在这里可以安全的使用sp指针.
}
else
{
   std::cout << “指针已被释放!” << std::endl;
}
};
int main()
{
    boost::shared_ptr<tester> sp1(new tester);
    boost.weak_ptr<tester> wp(sp1);
    // 开启两个线程,并将智能指针传入使用。
      boost::thread t1(boost::bind(&fun, wp));
       boost::thread t2(boost::bind(&fun, wp));
 
       t1.join();
      t2.join();
    return 0;
} 

weak_ptr指针功能一点都不weak,weak_ptr是一种可构造、可赋值以不增加引用计数来管理shared_ptr的指针,它可以方便的转回到shared_ptr指针,使用weak_ptr.lock函数就可以得到一个shared_ptr的指针,如果该指针已经被其它地方释放,它则返回一个空的shared_ptr,也可以使用weak_ptr.expired()来判断一个指针是否被释放。 boost.weak_ptr不仅可以解决多线程访问带来的安全问题,而且还可以解决上面第三个问题循环引用。Children类代码修改如下,即可打破循环引用:

class child
{
public:
       ~child() { 
      std::cout <<"子类析构函数被调用.\n"; 
       }
 
public:
       boost::weak_ptr<parent> parent;
};

因为weak_ptr不增加引用计数,所以可以在退出函数域时,正确的析构。

举报

相关推荐

0 条评论