标签:des http io ar os 使用 sp java strong
我们在程序运行的过程中,经常出现段错误、内存持续增大等,是C++显式内存管理存在的问题,主要归纳为以下几点:
针对以上1~3的问题,C++标准中提供了智能指针来解决。
智能指针是基于RAII(Resource Acquisition Is Initialization)机制实现的类(模板),具有指针的行为(重载了operator*与operator->操作符)。当对象创建的时候,进行初始化;离开其作用域后,通过自动调用析构函数释放资源。
C++98中,智能指针通过一个模板类型"auto_ptr"来实现,auto_ptr对象通过初始化指向由new创建的动态内存,当auto_ptr对象生命周期结
束时,其析构函数会将auto_ptr对象拥有的动态内存自动释放。即使发生异常,通过异常的栈展开过程也能将动态内存释放。但其有一些缺点:
auto_ptr< int > ap1( new int ( 100 )); auto_ptr< int > ap2(ap1); // ap1 == nullptr; |
auto_ptr 不能用在stl容器中,因为stl容器要求存储的类型必须为值语义类型。即两个对象在拷贝或赋值之后相等(ap1 == ap2)
~auto_ptr() { delete _M_ptr; } |
因此,在C++11标准中,改用unique_ptr、shared_ptr及weak_ptr等智能指针来对动态内存进行管理。auto_ptr为了兼容以前的代码被遗留下来,不建议使用。
<memory>
std
#include <iostream> #include <memory> #include <vector> using namespace std; struct Foo { Foo() {} ~Foo() {} void Print() { cout << "Foo" << endl; } }; int main( void ) { Foo* p1 = new Foo(); unique_ptr<Foo> up1; // up1==nullptr // up1 = p1; // 编译错误,不支持这样赋值 up1.reset(p1); // 替换管理对象,并释放之前管理的对象 p1 = nullptr; // unique_ptr<Foo> up2(up1); // 编译错误,不支持这样构造 unique_ptr<Foo> up2(std::move(up1)); // up1所有权转移到up2。up1==nullptr up1.swap(up2); // up2与up1管理对象的指针交换。 up2==nullptr if (up1) { // up1 != nullptr up1->Print(); // unique_ptr重载了-> (*up1).Print(); // unique_ptr重载了* } // up2->Print(); // 错误 up2 == nullptr, 必须先判断再调用 p1 = up1.get(); // get() 返回所管理对象的指针, up1继续持有其管理权 p1 = up1.release(); // release() 返回管理对象的指针,并释放管理权,up1==nullptr delete p1; unique_ptr<Foo> up3( new Foo()); up3.reset(); // 显示释放释放管理对象的内存,也可以这样做:up = nullptr; vector<unique_ptr<Foo>> v; unique_ptr<Foo> up4( new Foo()); // v.push_back(up4); // 编译错误,不支持这样拷贝 v.push_back(std::move(up4); // 只能up4放弃对其所有权,通过std::move()将所有权转移到容器中 return 0 ; } |
#include <iostream> #include <memory> #include <vector> #include < assert .h> using namespace std; struct Foo { int v; }; int main( void ) { shared_ptr<Foo> sp1( new Foo{ 10 }); cout << sp1.unique() << endl; // 1 当前shared_ptr唯一拥有Foo管理权时,返回true,否则返回false cout << sp1.use_count() << endl; // 1 返回当前对象的引用计数 shared_ptr<Foo> sp2(sp1); assert (sp1->v == sp2->v); // sp1与sp2共同拥有Foo对象 cout << sp2.unique() << endl; // 0 false cout << sp2.use_count() << endl; // 2 sp1.reset(); // 释放对Foo的管理权,同时引用计数减1 assert (sp1 == nullptr); // sp1 为空 cout << sp1.unique() << endl; // 0 不会抛出异常 cout << sp1.use_count() << endl; // 0 不会抛出异常 cout << sp1.get() << endl; // 0 不会跑出异常 // cout << sp1->v << endl; // 执行错误 sp1为nullptr时,operator* 和 operator-> 都会导致未定义行为 cout << sp2.unique() << endl; // 1 true sp1.swap(sp2); // sp1与sp2交换管理权,及引用计数 assert (sp2 == nullptr); cout << (*sp1).v << endl; // 10 同sp1->v相同 vector<shared_ptr<Foo>> vec; vec.push_back(sp1); cout << sp1.use_count() << endl; // 2 return 0 ; // vector先析构,里面存储的对象引用计数减1,但不为0,不释放对象 // sp1后析构,引用计数减1,变为0,释放所指对象的内存资源 } |
T& operator*() const ; T* operator->() const ; T* get() const ; bool unique() const ; long use_count() const ; void swap(shared_ptr<T>& b); |
1. 在一个map(unordered_map)中,多key索引一个value,使用shared_ptr。
#include <iostream> #include <memory> #include <map> #include <cstdint> using namespace std; Class SessionNode { public : SessionNode() {} virtual ~SessionNode() {} }; typedef std::shared_ptr<SessionNode> SessionNodeSP; int main( void ) { map<uint64_t, SessionNodeSP> map; uint64_t imsi = 4600000 ; uint32_t tmsi = 0x12345 ; { SessionNodeSP sp( new SessionNode()); map[imsi] = sp; map[tmsi] = sp; cout << sp.use_count() << endl; // 3 } // sp 销毁,引用计数减1,变为2 map.erase(tmsi); // use_count()为1 map.erase(imsi); // use_count()为0,释放被管理对象的内存 return 0 ; } |
2. 多个map索引同一value
#include <iostream> #include <memory> #include <map> #include <cstdint> using namespace std; struct Node { uint64_t imsi; uint32_t tmsi; }; typedef std::shared_ptr<Node> NodeSP; class Session { public : Session() {} ~Session() {} NodeSP GetNode(uint64_t imsi, uint32_t tmsi) { NodeSP sp( new Node{imsi, tmsi}); imsi_map_[imsi] = sp; tmsi_map_[tmsi] = sp; return sp; } void EraseNode(NodeSP& sp) { if (sp == nullptr) return ; imsi_map_.erase(sp->imsi); imsi_map_.erase(sp->tmsi); } private : map<uint64_t, NodeSP> imsi_map_; map<uint32_t, NodeSP> tmsi_map_; }; int main( void ) { Session ses; uint64_t imsi = 4600000 ; uint32_t tmsi = 0x12345 ; NodeSP sp; sp = ses.GetNode(imsi, tmsi); cout << sp.use_count() << endl; // 3 // ... do something with sp ses.EraseNode(sp); cout << sp.use_count() << endl; // 1 sp.reset(); // 主动释放被管理对象内存 return 0 ; } |
3. 防止裸指针被删除
#include <iostream> #include <memory> class Cdr { public : Cdr() {} protected : virtual ~Cdr() {} }; class SignalCdr : public Cdr { public : SignalCdr() {} virtual ~SignalCdr() {} }; typedef std::shared_ptr<Cdr> CdrSP; CdrSP CreateSignalCdr() { CdrSP sp( new SignalCdr()); return sp; }; int main( void ) { CdrSP sp_cdr = CreateSignalCdr(); SignalCdr* p_signal_cdr = reinterpret_cast<SignalCdr*>(sp_cdr.get()); // ... do something // delete p_signal_cdr; // 执行错误,~Cdr()为protected return 0 ; } |
4. 定制删除器。
#include <iostream> #include <memory> #include <errno.h> #include <stdio.h> #include <string.h> using namespace std; class FileCloser { public : void operator()(FILE* p) { // ... do something cout << "close file" << endl; fclose(p); } }; int main( void ) { try { FILE* p_file = fopen( "./test.cpp" , "r" ); if (p_file == nullptr) throw errno; shared_ptr<FILE> sp_file(p_file, FileCloser()); // shared_ptr<FILE> sp_file(p_file, &fclose); //如果只需要调用一个单参数的函数,可以直接这么写 // ... do something } catch ( int & err) { cout << strerror(err) << endl; } return 0 ; } |
5. 在引起循环引用的时候使用weak_ptr。
#include <iostream> #include <memory> using namespace std; struct Husband; struct Wife; typedef std::shared_ptr<Husband> HusbandSP; typedef std::shared_ptr<Wife> WifeSP; struct Wife { ~Wife() { cout<< "wife distroy" << endl; } HusbandSP sp_hb; }; struct Husband { ~Husband() { cout<< "husband distroy" << endl; } WifeSP sp_wf; }; int main( void ) { { HusbandSP husband( new Husband()); WifeSP wife( new Wife()); husband->sp_wf = wife; wife->sp_hb = husband; } // husband 和 wife,相互引用,离开作用域时引用计数都为1,造成内存泄露 return 0 ; } |
#include <iostream> #include <memory> using namespace std; struct Cdr{ int v; }; typedef std::shared_ptr<Cdr> CdrSP; typedef std::weak_ptr<Cdr> CdrWP; void UpdateCdr(CdrWP wp) { if (wp.expired() == false ) { // 检查被管理对象是否被删除,true 删除,false 没被删除;比use_count()==1要快 CdrSP sp = wp.lock(); // 提升为强引用 // CdrSP sp(wp); // 另一种wp提升为强引用方法 if (sp != nullptr) { // 若提升失败,shared_ptr 为 nullptr,此例子不会失败 sp->v *= 2 ; cout << sp.use_count() << endl; // 此时引用计数为2 } } // sp删除,引用计数减1 wp.reset(); // 显示释放所有权,或者等离开作用域会自动释放 } int main( void ) { CdrSP sp( new Cdr{ 10 }); UpdateCdr(sp); // 对sp进行操作 cout << sp->v << endl; // 20 return 0 ; } |
long use_count() const ; bool expired() const ; std::shared_ptr<T> lock() const ; |
#include <iostream> #include <memory> #include <map> using namespace std; struct Cdr { int v; }; typedef std::shared_ptr<Cdr> CdrSP; typedef std::weak_ptr<Cdr> CdrWP; class Cache { public : Cache() {} ~Cache() {} void UpdateIndex(CdrSP& sp) { if (sp != nullptr && sp->v > 0 ) cdr_map_[sp->v] = sp; } private : map< int , CdrWP> cdr_map_; }; int main( void ) { Cache cache; CdrSP sp_cdr( new Cdr{ 1 }); cache.UpdateIndex(sp_cdr); cout << sp_cdr.use_count() << endl; // 1 return 0 ; // sp_cdr销毁,引用计数为1,释放管理对象内存 // cache销毁,因为map中存储的为weak_ptr,weap_ptr为空,所以不会产生二次释放 } |
当shared_ptr通过weak_ptr参数构造,而weak_ptr指向一个已经被删除的对象时,会抛出std::bad_weak_ptr异常。
#include <iostream> #include <memory> using namespace std; int main() { shared_ptr< int > sp1( new int ( 1 )); weak_ptr< int > wp(sp1); p1.reset(); try { shared_ptr< int > sp2(wp); } catch ( const std::bad_weak_ptr& e) { cout << e.what() << endl; // "std::bad_weak_ptr" } } |
有时候,需要从this获得 shared_ptr ,即是说,你希望你的类被shared_ptr所管理,你需要把"自身"转换为shared_ptr的方法。
#include <iostream> #include <memory> using namespace std; class Foo; typedef std::shared_ptr<Foo> FooSP; void DoSomething( const FooSP& sp) { cout << sp.use_count() << endl; // 2 } class Foo : public std::enable_shared_from_this<Foo> { public : Foo() {} ~Foo() {} void Do() { DoSomething(shared_from_this()); } }; int main( void ) { FooSP sp( new Foo()); cout << sp.use_count() << endl; // 1 sp->Do(); return 0 ; } |
{ int * p = new int ( 100 ); auto_ptr< int > ap1(p); auto_ptr< int > ap2(p); } |
当离开作用域,ap1和ap2都试图删除p,会造成double free。
英文网址:
http://en.cppreference.com/w/cpp/memory
中文网址(google 机器翻译):
http://zh.cppreference.com/w/cpp/memory
标签:des http io ar os 使用 sp java strong
原文地址:http://www.cnblogs.com/457220157-FTD/p/4129058.html