C/C++编程:智能指针
计算机系统中的资源有很多种,内存是我们最常用到的,此外还有文件描述符,socket、操作系统handler,数据库连接等,程序里申请这些资源之后必须及时归坏系统,否则就会产生难以预料的后果
std::unique_ptr<char[]> buffer(new char[1024]);
文章目录
- 疑惑
- 引用与非引用做返回值(shared_ptr)
- 引用&非引用做参数(shared_ptr)
- RAII机制
- 显式内存管理
- 智能指针
- scoped_ptr
- 类摘要
- 操作函数
- 实践
- 源码阅读
- unique_ptr
- shared_ptr
- 实践
- 初始化
- 关于get()函数
- 关于make_shared函数
- shared_ptr的拷贝和赋值
- shared_ptr作返回值
- 容器中的shared_ptr
- shared_ptr对象的销毁
- 状态共享
- 智能指针与异常
- 源码
- weak_ptr
- 用法
疑惑
引用与非引用做返回值(shared_ptr)
#include <fstream> // ifstream, ifstream::in
#include <iostream>
#include <fcntl.h>
#include <zconf.h>
#include <memory>
#include <map>using namespace std;struct Foo{Foo(){printf("%s", "Foo:Foo\n");}~Foo(){printf("%s", "~Foo:Foo\n");}void foo(){printf("%s", "====Foo:foo\n");}
};std::map<std::string , std::shared_ptr<struct Foo>> m_dev_info;void init(){std::shared_ptr<struct Foo> dev_info = make_shared<struct Foo>();m_dev_info["aaa"] = dev_info;
}shared_ptr<struct Foo> & get_dev_yinyong(const std::string& camera_id){ //引用计数不加1return m_dev_info["aaa"];
}shared_ptr<struct Foo> get_dev_noyinyong(const std::string& camera_id){ // 引用计数加1return m_dev_info["aaa"];
}
int main(){init();printf("%ld\n", m_dev_info["aaa"].use_count());shared_ptr<struct Foo> dev_info;// shared_ptr<struct Foo> & i = get_dev_yinyong("aaa");
// printf("%ld\n", m_dev_info["aaa"].use_count());shared_ptr<struct Foo> f = get_dev_noyinyong("aaa");printf("%ld\n", m_dev_info["aaa"].use_count());getchar();return 0;
}
引用&非引用做参数(shared_ptr)
#include <fstream> // ifstream, ifstream::in
#include <iostream>
#include <fcntl.h>
#include <zconf.h>
#include <memory>
#include <map>using namespace std;struct Foo{Foo(){printf("%s", "Foo:Foo\n");}~Foo(){printf("%s", "~Foo:Foo\n");}void foo(){printf("%s", "====Foo:foo\n");}
};std::map<std::string , std::shared_ptr<struct Foo>> m_dev_info;void init(){std::shared_ptr<struct Foo> dev_info = make_shared<struct Foo>();m_dev_info["aaa"] = dev_info;
}bool get_dev_info(const std::string& camera_id, shared_ptr<struct Foo> &infoTag){ //引用计数+1infoTag = m_dev_info["aaa"];return true;
}bool get_dev_info1(const std::string& camera_id, shared_ptr<struct Foo> infoTag){ // 引用计数不加1infoTag = m_dev_info["aaa"];return true;
}
int main(){init();printf("%ld\n", m_dev_info["aaa"].use_count());shared_ptr<struct Foo> dev_info;get_dev_info("aaa", dev_info);printf("%ld\n", m_dev_info["aaa"].use_count());getchar();return 0;
}
RAII机制
为了管理内存等资源,C++程序员中通常采用RAII机制(资源获取即初始化),在类的构造函数中申请资源,然后使用,最后在析构函数中释放资源。
- 如果对象使用声明在栈上构建的,那么当对象离开作用域时对象会自动销毁从而调用析构函数释放资源
- 如果对象是用new在堆上创建的,那么必须由程序员手动的调用delete操作符销毁它才能释放资源。如果忘记释放,就会存在资源泄漏的隐患;还有种可能是因为某些意外导致程序未能执行delete语句,也会造成内存丢失。
显式内存管理
实际编写C/C++程序的时候,常会遇到比如程序允许是忽然退出,或者占用的内存越来越多,最后不得不定期重启的一些现象。这些问题的源头可以追溯到C/C++中的显式堆内存管理上(原因一般是没有正确的处理堆内存的分配和是否)。问题一般由三个:
- 野指针:一些内存单元已经被释放,之前指向它的指针却还在被使用。这些内存有可能被运行时系统重新分配给了程序使用,从而导致无法预测的错误。
- 重复释放:程序试图去释放已经释放过的内存单元,或者释放已经被重新分配过的内存单元,就会导致重复释放错误。通常重复释放内存会导致C/C++运行时系统打印出大量错误和诊断信息
- 内存泄漏:不需要再使用的内存单元如果没有被释放就会导致内存泄漏。如果程序不断地重复进行这类操作,将会导致内存占用剧增。
虽然显式的管理内存在性能上有一些优势,但是也非常容器出错。因此一个理念就是编程语言应该提供更好的机制,让程序员摆脱内存管理的细节。在C++中,一个这样的机制就是标准库中的智能指针。进一步的,标准库还提供了所谓”最小垃圾回收“支持。
智能指针
智能指针可以让对象在退出作用域时(不管是正常离开而是异常离开),调用delete来析构在堆上动态分配的对象
boost.smart_ptr库提供了六种智能指针:scoped_ptr
,shared_ptr
、weak_ptr
、instrusive_ptr
,scoped_array
、shared_array
、
- 它们都是轻量级的对象,速度和原始指针相差几无,都是异常安全(
exception safe
)的 - 而且对于所指向的类型T也仅有一个很小很合理的要求:类型T的析构函数不能抛出异常
scoped_array
、shared_array
很少用。
C++标准库提供的智能指针:weak_ptr
、shared_ptr
、unique_ptr
。它们是基于boost的smart_ptr库的
boost库的只能指针都位于名字空间boost
,需要包含头文件#include <boost/smart_ptr.hpp>
,即使:
#include <boost/smart_ptr.hpp>
using namespace boost;
补充:
auto_ptr
:最早的智能指针, 是C++98标准化引入的- auto_ptr虽说简单,但使用起来却到处是坑,以至于大家都不提倡使用(无论在什么情况下都不要使用)
std::auto_ptr
已在标准库中声明为飞起,现在应该使用新的智能指针std::unique_ptr
scoped_ptr
- 这个智能指针只能在本作用域里面使用,不能被转让
- 它包装了new操作符在堆上分配的动态对象,能够保证动态创建的对象在任何时候都可以被正确的删除。
类摘要
操作函数
scoped_ptr
的构造函数- 接受一个类型为T*的指针p,创建出一个scoped_ptr对象,并在内部参数保存指针参数p。p必须满足下面两种要求:
- p要么是一个new表达式动态分配的结果
- p要么是一个空指针nullptr
- 当scoped_ptr对象的生命期结束时,析构函数对使用delete操作符自动销毁所保存的对象,从而正确的回收资源
- 接受一个类型为T*的指针p,创建出一个scoped_ptr对象,并在内部参数保存指针参数p。p必须满足下面两种要求:
scoped_ptr
把拷贝构造函数和赋值函数都声明为私有的,禁止拷贝或者赋值,从而保证了被它管理的指针不能转让所有权,只能在scoped_ptr
声明的作用域内使用reset
: 重置scoped_ptr, 它删除了原来保存的指针,再保存新的指针值p。- 如果p是空指针,那么scoped_ptr将不再持有任何指针
- 一般情况下,reset不应该被调用,因为它违背了scoped_ptr的本意–》资源应该一直由scoped+pty自己自动管理。
实践
例子:
#include <boost/smart_ptr.hpp>
using namespace std;
using namespace boost;class X
{public:X(){cout << "X ..." << endl;}~X(){cout << "~X ..." << endl;}
};
int main(int argc,char *argv[]){cout << "Entering main ..." << endl;{boost::scoped_ptr<X> pp( new X);//boost::scoped_ptr<X> p2(pp); //Error:所有权不能转移}cout << "Exiting main ..." << endl;return 0;
}
例子
#include <boost/smart_ptr.hpp>
using namespace std;
using namespace boost;int main(int argc,char *argv[]){scoped_ptr<string> scopedPtr(new string("text")); // scoped_ptr是一个对象而不是指针,没有办法delete的。assert(scopedPtr != nullptr);cout << *scopedPtr << endl; // text : * 取出字符串的内容cout << scopedPtr->size() << endl; //4 : -> 字符串的长度scopedPtr.reset();assert(scopedPtr == nullptr);return 0;
}
源码阅读
template<class T> class scoped_ptr // noncopyable
{private:T * px; // 原始指针// 不能拷贝/赋值:保证了被它管理的指针不能被转让所有权scoped_ptr(scoped_ptr const &); // 拷贝构造函数私有化--->不能拷贝scoped_ptr & operator=(scoped_ptr const &); // 赋值函数私有化 ---> 不能赋值typedef scoped_ptr<T> this_type;void operator==( scoped_ptr const& ) const; // 相等函数私有化void operator!=( scoped_ptr const& ) const; // 不等函数私有化public:typedef T element_type;/*接受一个类型为T*的指针p,创建出一个scoped_ptr对象,并在内部参数保存指针参数pp必须是一个new表达式动态分配的结果,或者一个空指针nullptr当scoped_ptr对象的生命期结束时,析构函数对使用delete操作符自动销毁所保存的对象,从而正确的回收资源*/explicit scoped_ptr( T * p = 0 ): px( p ) // never throws // 显式构造函数{} explicit scoped_ptr( std::auto_ptr<T> p ) BOOST_NOEXCEPT : px( p.release() ){}~scoped_ptr() // never throws // 析构函数{boost::checked_delete( px );}// 重置scoped_ptr, 它删除了原来保存的指针,再保存新的指针值p。// 如果p是空指针,那么scoped_ptr将不再持有任何指针// 一般情况下,reset不应该被调用,因为它违背了scoped_ptr的本意--》资源应该一直由scoped+pty自己自动管理。void reset(T * p = 0) // never throws // 重置智能指针{BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errorsthis_type(p).swap(*this);}// * 和->重载的意义: 模仿被代理的指针的行为T & operator*() const // never throws //操作符重载{BOOST_ASSERT( px != 0 ); // BOOST_ASSERT仅工作再debug模式下return *px;}T * operator->() const // never throws //操作符重载{BOOST_ASSERT( px != 0 );return px;}// 用在某些要求必须是原始指针的场景,但是使用时必须小写,这将使原始指针脱离scoped_ptr的控制,不能对这个指针deleteT * get() const BOOST_NOEXCEPT // 获取原始指针{return px;}// implicit conversion to "bool"
#include <boost/smart_ptr/detail/operator_bool.hpp>void swap(scoped_ptr & b) BOOST_NOEXCEPT {T * tmp = b.px;b.px = px;px = tmp;}
};
unique_ptr
std::unique_pt形如起名,是一种独占的智能指针,与所指对象的内存绑定紧密, 禁止其他智能指针与其共享同一个对象,从而导致代码的安全:
auto pointer = std::make_unique<int>(10); //C++14引入auto p1 = pointer; // 非法
既然是独占,也就是说不可复制。但是,我们可以利用std::move将其转移给其他unique_ptr,一旦被转移,就不再拥有,除非被显示的归还。
例如:
#include <iostream>
#include <memory>struct Foo{Foo(){printf("%s", "Foo:Foo\n");}~Foo(){printf("%s", "~Foo:Foo\n");}void foo(){printf("%s", "====Foo:foo\n");}
};void f(const Foo &){printf(" f(const Foo &)\n");
}int main(){std::unique_ptr<Foo> p1(std::make_unique<Foo>());// p1不空,输出if (p1){p1->foo();}{std::unique_ptr<Foo> p2(std::move(p1));f(*p2); // f(const Foo &)if(p2){p2->foo(); // ====Foo:foo}if (p1){p1->foo(); // std::move给了p2,因此无输出}p1 = std::move(p2);if(p2){p2->foo(); // std::move给了p1,因此无输出}if (p1){p1->foo(); // ====Foo:foo}p2 = std::move(p1);printf("p2被销毁\n");}if (p1){p1->foo(); // ====Foo:foo}return 0;
}
从实现上来讲,unique_ptr
是一个删除了拷贝构造函数、保留了移动构造函数的指针封装类型。程序员仅可以使用右值对unique_ptr对象进行构造,而且一旦构造成功,右值对象中的指针即被“窃取”,因此,该右值对象马上失去了对指针的“所有权”
shared_ptr
shared_ptr
是一个最像指针的智能支持,被c++11标准收入shared_ptr
是一种智能指针,它能够记录多少个shared_ptr共同指向同一个对象,从而消除显式调用的delete,当引用计数变成0时就会将对象自动删除shared_ptr
与scoped_ptr
一样,包装了new操作符在堆上分配的动态对象,但它实现的是引用计数性的智能指针,可以被自由的拷贝和赋值,在任意地方共享它,当没有代码使用(引用计数为0)它时才被包装的动态分配的对象释放- 因为使用
std::shared_ptr
仍然需要new来调用,这使得代码出现了不对称,std::make_shared
就能够消除显式的调用new,所以std::make_shared会分配创建传入参数中的对象,并返回这个对象类型的std::shared_ptr指针
实践
#include <memory>
#include <assert.h>
#include <map>
#include <iostream>using namespace std;
class Shape{public:Shape() {cout<<"Base::Draw()"<<endl;}~Shape() {cout<<"Base::Erase()"<<endl;}
};class Polygon:public Shape{public:Polygon() {cout<<"Polygon::Draw()"<<endl;}~Polygon() {cout<<"Polygon Erase()"<<endl;}
};class Rectangle:public Polygon{public:Rectangle() {cout<<"Rectangle::Draw()"<<endl;}~Rectangle() {cout<<"Rectangle Erase()"<<endl;}
};
初始化
{//一般的初始化方式shared_ptr<string> pint_s(new string("normal usage!"));cout<<*pint_s<<endl;//推荐的安全的初始化方式shared_ptr<string> pint_s1 = make_shared<string>("safe uage!");cout<<*pint_s1<<endl;
}
{//一般的初始化方式(不推荐)shared_ptr<Shape> pint(new Shape());}{//推荐的安全的初始化方式shared_ptr<Shape> pint1 = make_shared<Shape>();}{// 变量类型太长,可以重命名(推荐)typedef shared_ptr<Shape> sp_t;sp_t sp2 = make_shared<Shape>();}
typedef shared_ptr<Shape> sp_t;sp_t sp1 = make_shared<Polygon>();
托管指针
int main6()
{shared_ptr<Shape> sp1= make_shared<Shape>();
} // Base::Draw() ---> 由sp1托管shared_ptr<Shape> sp2(sp1); // 同时交由sp2托管shared_ptr<Shape> sp3;sp3 = sp1; // 同时交由sp3托管// 注意,不能用下面的方式使得两个 shared_ptr 对象托管同一个指针:/* Shape* p = new Shape;shared_ptr <Shape> sp4(p), sp5(p);sp4 和 sp5 并不会共享同一个对 p 的托管计数,而是各自将对 p 的托管计数都记为 1(sp5 无法知道 p 已经被 sp4 托管过)。这样,当 sp4 消亡时要析构 p,sp5 消亡时要再次析构 p,这会导致程序崩溃。
*/return 0;
}
智能指针的使用方式与普通指针类似。解引用一个智能指针返回它指向的对象。如果在一个条件判断中使用智能指针,效果就是检测它是否为空
#include <iostream>
#include <memory>
using namespace std;int main()
{/*---------空指针------------*/shared_ptr<string> p1;if(!p1) //!默认初始化的智能指针中保存着一个空指针!并不是""空字符串cout<<"p1==NULL"<<endl;/*---------初始化------------*/shared_ptr<string> p2(new string);if(p2&&p2->empty()){ //!需要注意的时empty时属于string的成员函数。*p2="helloworld";cout<<*p2<<endl;}shared_ptr<int> spi(new int); // 一个int类型的shared_ptrassert(spi); // bool类型转换判断指针的有效性*spi = 256; shared_ptr<std::string> sps(new std::string("smart"));assert(sps->size() == 5);// shared_ptr<int> pa = new int(1);//!error:不允许以暴露裸漏的指针进行赋值操作。//一般的初始化方式shared_ptr<std::exception> sp1(new std::bad_exception());auto sp2 = dynamic_pointer_cast<std::bad_exception>(sp1);auto sp3 = static_pointer_cast<std::exception>(sp2);assert(sp3 == sp1);/**********************不推荐***********************/int * p = new int(32);shared_ptr<int> pp(p);cout<<*pp<<endl;/*意外的情况*/
// delete p; //!不小心把delete掉了。
// cout<<*pp<<endl;· //!pp也不再有效。
}
关于get()函数
智能指针定义了一个名为get的函数,它返回一个内置指针,指向智能指针的管理的对象。此函数设置的初衷是当我们向不能使用智能指针的代码传递一个内置指针。使用get返回指针的代码不能delete此指针。
useShared_ptr(p1.get());
// delePointer(p1.get()); //!error:
也就是说,std::shared_ptr可以通过get()
方法来获取原始指针,通过reset()
来减少一个引用计数,并通过使用use_count
来查看一个对象的引用计数
#include <iostream>
#include <memory>int main(){auto pointer = std::make_shared<int>(10);auto pointer2 = pointer; // 引用计数 +1auto pointer3 = pointer; // 引用计数 +1int *p = pointer.get(); // 这样不会增加引用计数std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3pointer2.reset();std::cout << "reset pointer2:" << std::endl;std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0, pointer2 已 resetstd::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2pointer3.reset();std::cout << "reset pointer3:" << std::endl;std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;return 0;
}
再次声明:
- get用来将指针的访问权限传递给代码,只有在确定代码不会delete指针的情况下,才能使用get。
- 特别是,永远不要用get初始化另一个智能指针或者为另一个智能指针赋值!
关于make_shared函数
最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数,此函数在动态内存中分配一个对象并初始化它,返回此对象的shared_ptr。与智能指针一样,make_shared也定义在头文件memory中。
#include <iostream>
using namespace std;int main()
{shared_ptr<int> p3 = make_shared<int>(42);cout<<*p3<<endl;shared_ptr<string> pstr = make_shared<string>("99999");cout<<*pstr<<endl;shared_ptr<int> pint = make_shared<int>(); //!默认初始化为 0cout<<*pint<<endl;auto pau = make_shared<string>("auto"); //!更简单,更常用的方式。cout<<*pau<<endl;
}
使用make_shared用其参数来构造给定类型的对象;传递的参数必须能够与该类型的某个构造函数相匹配。
通常我们用auto来定义一个对象来保存make_shared的结果,这种方式更为简单。
shared_ptr的拷贝和赋值
当进行拷贝或者赋值操作时,每个shared_ptr都会记录有多少个其他的shared_ptr指向相同的对象:
#include <iostream>
#include <memory>
using namespace std;int main()
{auto p = make_shared<int>(42); //!p指向的对象只有p一个引用者。cout<<p.use_count()<<endl; // 1 //返回与p共享对象的智能指针数量;可能很慢,主要用于调试。cout<<p.unique()<<endl; // 若p.use_count()为1,则返回 true;否则返回 falseauto q(p); //!p和q指向相同的对象,此对象有两个引用者。cout<<p.use_count()<<endl; // 2cout<<q.use_count()<<endl; // 2return 0;
}
shared_ptr作返回值
#include <iostream>
#include <memory>
using namespace std;shared_ptr<string> factory(const char * p){return make_shared<string>(p);
}void user_factory(){shared_ptr<string> p = factory("helloworld"); // p和return返回的时同一个对象cout<<p.use_count() <<endl; //1cout<<*p<<endl; //!离开作用域时,p引用的对象被销毁。
}shared_ptr<string> return_share_ptr()
{shared_ptr<string> p = factory("helloworld");cout<<*p<<endl;return p; //!返回p时,引用计数进行了递增操作。
} //!p离开了作用域,但他指向的内存不会被释放掉。int main()
{user_factory();auto p = return_share_ptr();cout<<p.use_count()<<endl; // 1return 0;
}
容器中的shared_ptr
对于一块内存,shared_ptr类保证了只要有share_ptr对象引用它,他就不会被释放掉。由于这个特性,保证shared_ptr
在不用之后不再保留就非常重要了,通常这个过程能够自动执行而不需要人工干预。有一种例外是我们将shared_ptr放在了容器中。所以永远不要忘记erease不用的shared_ptr
#include <iostream>
#include <memory>
#include <list>
#include <map>
using namespace std;int main()
{map<string, shared_ptr<string>> mapptr;mapptr["aaaa"] = make_shared<string>("11111");mapptr["bbbb"] = make_shared<string>("2222");mapptr.erase("aaaa");std::cout << mapptr["aaaa"] << "\t";std::cout << mapptr["bbbb"] << "\n";list<shared_ptr<string>>pstrList;pstrList.push_back(make_shared<string>("1111"));pstrList.push_back(make_shared<string>("2222"));pstrList.push_back(make_shared<string>("3333"));pstrList.push_back(make_shared<string>("4444"));for(auto itr = pstrList.begin() ;itr!=pstrList.end();){// erase() 接受一个 iterator 作为入参,可从list或vector中删除该 iterator 所指向的元素。但同时传入的该 iterator 即失效!!if(**itr == "3333"){itr = pstrList.erase(itr);}else{itr++;}}cout<<"-------------after remove------------"<<endl;for(auto p:pstrList){cout<<*p<<endl;}return 0;
}
shared_ptr对象的销毁
- 管理动态数组
默认情况下,shared_ptr指向的动态的内存是使用delete来删除的。这和我们手动去调用delete然后调用对象内部的析构函数是一样的。与unique_ptr不同,shared_ptr不直接管理动态数组。如果希望使用shared_ptr管理一个动态数组,必须提供自定义的删除器来替代delete 。
状态共享
使用shared_ptr在一个常见的原因是允许多个多个对象共享相同的状态,而非多个对象独立的拷贝!
#include <iostream>
#include <memory>
#include <list>
#include <map>
using namespace std;void copyCase()
{list<string> v1({"1","b","d"});list<string> v2 = v1; //!v1==v2占用两段内存v1.emplace_back("cc"); //!v1!=v2cout<< "copyCase : "<<v1.size() << "\t" << v2.size() <<endl;
} //v1和v2分属两个不同的对象,一个改变不会影响的状态。 // copyCase : 4 3void shareCase()
{shared_ptr<list<string>> v1 = make_shared<list<string>>(2,"bb");shared_ptr<list<string>> v2 = v1;(*v1).emplace_back("c2c");cout<<"shareCase : " <<v1->size() << "\t" << v2->size() <<endl;
} //v1和v2属于一个对象的两个引用,有引用计数为证,其内容的改变是统一的。 shareCase : 3 3
int main()
{copyCase();cout<<"++++++++++++++++"<<endl;shareCase();
}
智能指针与异常
异常发生后,常规的动态内存常常不能正确释放。但是如果使用智能指针,即程序过早结束,智能指针也能确保在内存不需要时将其释放:
void f()
{shared_ptr<int>sp(new int(42)) ;
}
函数的推出,要么有两种情况,正常处理结束或者发生了异常,无论哪种情况,局部对象都会被销毁。在上面的程序中,sp是一个shared_ptr,因此sp销毁时会检查引用计数。在此例中,sp是指向这块内存的唯一指针。所以会被释放掉。
与之相对的,当发生异常时,我们直接管理的内存时不会自动释放的,如果使用内置指针管理内存,且在new之后对应的delet之前发生异常,则内存不会释放。
void f()
{int *p = new int(42);//code//!异常抛出,且没有在f()中被捕获。delete p;
}
如果在new和delete之间发生异常,且异常未在f()中捕获,则内存就永远不会被释放了。
源码
template<class T> class shared_ptr
{private:// Borland 5.5.1 specific workaroundtypedef shared_ptr<T> this_type;public:typedef typename boost::detail::sp_element< T >::type element_type;shared_ptr() BOOST_NOEXCEPT : px( 0 ), pn() // never throws in 1.30+{}#if !defined( BOOST_NO_CXX11_NULLPTR )shared_ptr( boost::detail::sp_nullptr_t ) BOOST_NOEXCEPT : px( 0 ), pn() // never throws{}#endiftemplate<class Y>explicit shared_ptr( Y * p ): px( p ), pn() // Y must be complete{boost::detail::sp_pointer_construct( this, p, pn );}//// Requirements: D's copy constructor must not throw//// shared_ptr will release p by calling d(p)//template<class Y, class D> shared_ptr( Y * p, D d ): px( p ), pn( p, d ){boost::detail::sp_deleter_construct( this, p );}#if !defined( BOOST_NO_CXX11_NULLPTR )template<class D> shared_ptr( boost::detail::sp_nullptr_t p, D d ): px( p ), pn( p, d ){}#endif// As above, but with allocator. A's copy constructor shall not throw.template<class Y, class D, class A> shared_ptr( Y * p, D d, A a ): px( p ), pn( p, d, a ){boost::detail::sp_deleter_construct( this, p );}#if !defined( BOOST_NO_CXX11_NULLPTR )template<class D, class A> shared_ptr( boost::detail::sp_nullptr_t p, D d, A a ): px( p ), pn( p, d, a ){}#endif// generated copy constructor, destructor are fine...#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES )// ... except in C++0x, move disables the implicit copyshared_ptr( shared_ptr const & r ) BOOST_NOEXCEPT : px( r.px ), pn( r.pn ){}#endiftemplate<class Y>explicit shared_ptr( weak_ptr<Y> const & r ): pn( r.pn ) // may throw{boost::detail::sp_assert_convertible< Y, T >();// it is now safe to copy r.px, as pn(r.pn) did not throwpx = r.px;}template<class Y>shared_ptr( weak_ptr<Y> const & r, boost::detail::sp_nothrow_tag )BOOST_NOEXCEPT : px( 0 ), pn( r.pn, boost::detail::sp_nothrow_tag() ){if( !pn.empty() ){px = r.px;}}template<class Y>
#if !defined( BOOST_SP_NO_SP_CONVERTIBLE )shared_ptr( shared_ptr<Y> const & r, typename boost::detail::sp_enable_if_convertible<Y,T>::type = boost::detail::sp_empty() )#elseshared_ptr( shared_ptr<Y> const & r )#endifBOOST_NOEXCEPT : px( r.px ), pn( r.pn ){boost::detail::sp_assert_convertible< Y, T >();}// aliasingtemplate< class Y >shared_ptr( shared_ptr<Y> const & r, element_type * p ) BOOST_NOEXCEPT : px( p ), pn( r.pn ){}#ifndef BOOST_NO_AUTO_PTRtemplate<class Y>explicit shared_ptr( std::auto_ptr<Y> & r ): px(r.get()), pn(){boost::detail::sp_assert_convertible< Y, T >();Y * tmp = r.get();pn = boost::detail::shared_count( r );boost::detail::sp_deleter_construct( this, tmp );}#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES )template<class Y>shared_ptr( std::auto_ptr<Y> && r ): px(r.get()), pn(){boost::detail::sp_assert_convertible< Y, T >();Y * tmp = r.get();pn = boost::detail::shared_count( r );boost::detail::sp_deleter_construct( this, tmp );}#elif !defined( BOOST_NO_SFINAE ) && !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION )template<class Ap>explicit shared_ptr( Ap r, typename boost::detail::sp_enable_if_auto_ptr<Ap, int>::type = 0 ): px( r.get() ), pn(){typedef typename Ap::element_type Y;boost::detail::sp_assert_convertible< Y, T >();Y * tmp = r.get();pn = boost::detail::shared_count( r );boost::detail::sp_deleter_construct( this, tmp );}#endif // BOOST_NO_SFINAE, BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION#endif // BOOST_NO_AUTO_PTR#if !defined( BOOST_NO_CXX11_SMART_PTR )template< class Y, class D >shared_ptr( std::unique_ptr< Y, D > && r ): px( r.get() ), pn(){boost::detail::sp_assert_convertible< Y, T >();typename std::unique_ptr< Y, D >::pointer tmp = r.get();pn = boost::detail::shared_count( r );boost::detail::sp_deleter_construct( this, tmp );}#endif// assignmentshared_ptr & operator=( shared_ptr const & r ) BOOST_NOEXCEPT{this_type(r).swap(*this);return *this;}#if !defined(BOOST_MSVC) || (BOOST_MSVC >= 1400)template<class Y>shared_ptr & operator=(shared_ptr<Y> const & r) BOOST_NOEXCEPT{this_type(r).swap(*this);return *this;}#endif#ifndef BOOST_NO_AUTO_PTRtemplate<class Y>shared_ptr & operator=( std::auto_ptr<Y> & r ){this_type( r ).swap( *this );return *this;}#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES )template<class Y>shared_ptr & operator=( std::auto_ptr<Y> && r ){this_type( static_cast< std::auto_ptr<Y> && >( r ) ).swap( *this );return *this;}#elif !defined( BOOST_NO_SFINAE ) && !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION )template<class Ap>typename boost::detail::sp_enable_if_auto_ptr< Ap, shared_ptr & >::type operator=( Ap r ){this_type( r ).swap( *this );return *this;}#endif // BOOST_NO_SFINAE, BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION#endif // BOOST_NO_AUTO_PTR#if !defined( BOOST_NO_CXX11_SMART_PTR )template<class Y, class D>shared_ptr & operator=( std::unique_ptr<Y, D> && r ){this_type( static_cast< std::unique_ptr<Y, D> && >( r ) ).swap(*this);return *this;}#endif// Move support#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES )shared_ptr( shared_ptr && r ) BOOST_NOEXCEPT : px( r.px ), pn(){pn.swap( r.pn );r.px = 0;}template<class Y>
#if !defined( BOOST_SP_NO_SP_CONVERTIBLE )shared_ptr( shared_ptr<Y> && r, typename boost::detail::sp_enable_if_convertible<Y,T>::type = boost::detail::sp_empty() )#elseshared_ptr( shared_ptr<Y> && r )#endifBOOST_NOEXCEPT : px( r.px ), pn(){boost::detail::sp_assert_convertible< Y, T >();pn.swap( r.pn );r.px = 0;}shared_ptr & operator=( shared_ptr && r ) BOOST_NOEXCEPT{this_type( static_cast< shared_ptr && >( r ) ).swap( *this );return *this;}template<class Y>shared_ptr & operator=( shared_ptr<Y> && r ) BOOST_NOEXCEPT{this_type( static_cast< shared_ptr<Y> && >( r ) ).swap( *this );return *this;}#endif#if !defined( BOOST_NO_CXX11_NULLPTR )shared_ptr & operator=( boost::detail::sp_nullptr_t ) BOOST_NOEXCEPT // never throws{this_type().swap(*this);return *this;}#endifvoid reset() BOOST_NOEXCEPT // never throws in 1.30+{this_type().swap(*this);}template<class Y> void reset( Y * p ) // Y must be complete{BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errorsthis_type( p ).swap( *this );}template<class Y, class D> void reset( Y * p, D d ){this_type( p, d ).swap( *this );}template<class Y, class D, class A> void reset( Y * p, D d, A a ){this_type( p, d, a ).swap( *this );}template<class Y> void reset( shared_ptr<Y> const & r, element_type * p ){this_type( r, p ).swap( *this );}// never throws (but has a BOOST_ASSERT in it, so not marked with BOOST_NOEXCEPT)typename boost::detail::sp_dereference< T >::type operator* () const{BOOST_ASSERT( px != 0 );return *px;}// never throws (but has a BOOST_ASSERT in it, so not marked with BOOST_NOEXCEPT)typename boost::detail::sp_member_access< T >::type operator-> () const {BOOST_ASSERT( px != 0 );return px;}// never throws (but has a BOOST_ASSERT in it, so not marked with BOOST_NOEXCEPT)typename boost::detail::sp_array_access< T >::type operator[] ( std::ptrdiff_t i ) const{BOOST_ASSERT( px != 0 );BOOST_ASSERT( i >= 0 && ( i < boost::detail::sp_extent< T >::value || boost::detail::sp_extent< T >::value == 0 ) );return px[ i ];}element_type * get() const BOOST_NOEXCEPT{return px;}// implicit conversion to "bool"
#include <boost/smart_ptr/detail/operator_bool.hpp>bool unique() const BOOST_NOEXCEPT{return pn.unique();}long use_count() const BOOST_NOEXCEPT{return pn.use_count();}void swap( shared_ptr & other ) BOOST_NOEXCEPT{std::swap(px, other.px);pn.swap(other.pn);}template<class Y> bool owner_before( shared_ptr<Y> const & rhs ) const BOOST_NOEXCEPT{return pn < rhs.pn;}template<class Y> bool owner_before( weak_ptr<Y> const & rhs ) const BOOST_NOEXCEPT{return pn < rhs.pn;}void * _internal_get_deleter( boost::detail::sp_typeinfo const & ti ) const BOOST_NOEXCEPT{return pn.get_deleter( ti );}void * _internal_get_untyped_deleter() const BOOST_NOEXCEPT{return pn.get_untyped_deleter();}bool _internal_equiv( shared_ptr const & r ) const BOOST_NOEXCEPT{return px == r.px && pn == r.pn;}// Tasteless as this may seem, making all members public allows member templates
// to work in the absence of member template friends. (Matthew Langston)#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDSprivate:template<class Y> friend class shared_ptr;template<class Y> friend class weak_ptr;#endifelement_type * px; // contained pointerboost::detail::shared_count pn; // reference counter}; // shared_ptr
weak_ptr
weak_ptr是为了配合shared_ptr而引入的一种智能指针,它更像是
shared_ptr
的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载operator *
和->
。它的最大作用在于协助shared_ptr
工作,像旁观者那样观测资源的使用情况
std::shared_ptr还是会存在着资源无法释放的问题,如下:
#include <iostream>
#include <memory>
struct A;
struct B;
struct A {std::shared_ptr<B> pointer;~A() {std::cout << "A 被销毁" << std::endl;}
};
struct B {std::shared_ptr<A> pointer;~B() {std::cout << "B 被销毁" << std::endl;}
};
int main() {auto a = std::make_shared<A>();auto b = std::make_shared<B>();a->pointer = b;b->pointer = a;
}
运行结果是A,B都不会销毁,这是因为a, b内部的pointer同时又引用了a, b,这使得a, b的引用计数均变成了2,而离开作用域时,却只能造成这块区域的引用计数减1,这样就导致了a, b对象指向的内存区域引用计数不为0,而外部已经没有办法找到这块区域了,也就造成了内存泄漏,如下图:
解决这个问题的办法是使用弱引用指针std::weak_pttr,std::weak_ptr是一种弱引用(std::shared_ptr可以看成是强引用)。弱引用不会引起引用计数增加,当换用弱引用的时候,最终的释放流程如下:
在上图中,最后一步只剩下B,而B并没有任何智能指针引用它,因此这块内存资源也会被释放。
std::weak_ptr没有*运算符和->运算符,所以不能够对资源进行操作,它的唯一作用就是用于检查std::shared_ptr是否存在,其expired()方法能在资源未被释放时,返回false,否则返回true
用法
weak_ptr被设计为与shared_ptr协同工作,可以从一个shared_ptr
或者另一个weak_ptr
对象构造,获得资源的观测权。但weak_ptr`没有共享资源,它的构造不会引起指针引用计数的增加或者减少,它只是一个静静的观察者
参考
C/C++编程:智能指针相关推荐
- 共享智能指针编程实验
共享智能指针编程实验 基本知识 shared_ptr与make_shared initializer_list 自定义的StrBlob类 const限定符 示例代码 my_StrBlob.h main ...
- bartender一行打印两个二次开发_C++ 智能指针和二叉树:图解层序遍历和逐层打印二叉树...
作者:apocelipes 链接:https://www.cnblogs.com/apocelipes/p/10758692.html 二叉树是极为常见的数据结构,关于如何遍历其中元素的文章更是数不 ...
- 五点讲述C++智能指针的点点滴滴
(在学习C/C++或者想要学习C/C++可以加我们的学习交流QQ群:712263501群内有相关学习资料) 0.摘要 本文先讲了智能指针存在之前C++面临的窘境,并顺理成章地引出利用RAII技术封装普 ...
- 【Smart_Point】C/C++ 中智能指针
C++11智能指针 目录 C++11智能指针 1.1 C++11智能指针介绍 1.2 为什么要使用智能指针 1.2.1 auto_ptr(C++98的方案,C++11已经抛弃)采用所有权模式. 1.2 ...
- C++——智能指针——auto_ptr、shared_ptr、unique_ptr
1.4.智能指针 智能指针是行为类似于指针的类对象. C++11 中提供了三种智能指针,使用这些智能指针时需要引用头文件 : ·shared_ptr; ·unique_ptr; ·auto_ptr; ...
- C++ 智能指针详解
智能指针内容很多,重点是基本用法. #include <boost/shared_ptr.hpp> class CBase: public boost::enable_shared_fro ...
- C++ 智能指针的正确使用方式
C++11 中推出了三种智能指针,unique_ptr.shared_ptr 和 weak_ptr,同时也将 auto_ptr 置为废弃 (deprecated). 但是在实际的使用过程中,很多人都会 ...
- C++智能指针剖析(上)std::auto_ptr与boost::scoped_ptr
1. 引入 C++语言中的动态内存分配没有自动回收机制,动态开辟的空间需要用户自己来维护,在出函数作用域或者程序正常退出前必须释放掉. 即程序员每次 new 出来的内存都要手动 delete,否则会造 ...
- get方法报空指针_智能指针shared_ptr踩坑笔记
平时写代码一直避免使用指针,但在某些场景下指针的使用还是有必要的.最近在项目中简单使用了一下智能指针(shared_ptr),结果踩了不少坑,差点就爬不出来了.痛定思痛抱着<Cpp Primer ...
最新文章
- ThreadLocal模式的一点小理解
- 【2781】二分练习 sdutOJ
- python装饰器原理-深入理解 Python 装饰器
- JAVA课堂作业整理一
- Redis : redis事务
- cocos2dx之lua项目开发中MVC框架的简单应用
- python学习-字符串的基本操作
- JAVA入门级教学之(连接运算符)
- 博弈论 斯坦福game theory stanford week 3.2_
- paip.xdebug 配置attilax总结.txt
- NSGA-II 算法详解
- 局域网网络流量监控_LINUX常见性能监控工具总结
- 在Fedora 28上安装LimeSurvey CE
- java计算机毕业设计家教管理系统源码+mysql数据库+系统+lw文档+部署
- 2019计算机保研 中科院信工所夏令营+中科院软件所九推记录
- [Daozy][区块链 EOS 课程]第2课 EOS编译和启动
- C语言:7-20 到底有多二
- 小说更新太慢怎么办_写网络小说写得太慢怎么办?
- Windows第一次博客作业:委托实现信用卡用户定时还款
- 一篇不错的关于VSS的入门介绍
热门文章
- 计算机进去bios方式,两种使用的电脑进入BIOS方法
- SRAM、PSRAM、SPI FLASH初步认识
- 独木带你玩转彩屏——应用1驱动彩屏(寄存器spi版)
- Python读取图片内容并进行修改
- 几个不太常用,需要记录一下的Excel经验
- python3爬虫数据清洗与可视化实战pdf百度云_Python 3爬虫、数据清洗与可视化实战_PDF电子书...
- Win10系统磁盘扩展分区与恢复分区
- 奇门遁甲排盘方:定局
- 计算机校本培训措施,2017度信息技术校本培训计划
- mysql数据中包含不间断空格(ascii值为194和160)解决办法