网站建设设计大作业,创意设计网,如何做免费企业网站,深圳苏州旅游网站建设服务1. 智能指针介绍
为解决裸指针可能导致的内存泄漏问题。如#xff1a; a#xff09;忘记释放内存#xff1b; b#xff09;程序提前退出导致资源释放代码未执行到。
就出现了智能指针#xff0c;能够做到资源的自动释放。 2. 智能指针的原理和简单实现
2.1 智能指针的原…1. 智能指针介绍
为解决裸指针可能导致的内存泄漏问题。如 a忘记释放内存 b程序提前退出导致资源释放代码未执行到。
就出现了智能指针能够做到资源的自动释放。 2. 智能指针的原理和简单实现
2.1 智能指针的原理
将裸指针封装为一个智能指针类需要使用该裸指针时就创建该类的对象利用栈区对象出作用域会自动析构的特性保证资源的自动释放。
2.2 智能指针的简单实现
代码示例
templatetypename T
class MySmartPtr {
public:MySmartPtr(T* ptr nullptr):mptr(ptr) { // 创建该对象时裸指针会传给对象}~MySmartPtr() { // 对象出作用域会自动析构因此会释放裸指针指向的资源delete mptr;}// *运算符重载T operator*() { // 提供智能指针的解引用操作即返回它包装的裸指针的解引用return *mptr; }// -运算符重载T* operator-() { // 即返回裸指针return mptr;}
private:T* mptr;
};class Obj {
public:void func() {cout Obj::func endl;}
};void test01() {/*创建一个int型的裸指针使用MySmartPtr将其封装为智能指针对象ptrptr对象除了作用域就会自动调用析构函数。智能指针就是利用栈上对象出作用域自动析构这一特性。*/MySmartPtrint ptr0(new int);*ptr0 10;MySmartPtrObj ptr1(new Obj);ptr1-func();(ptr1.operator-())-func(); // 等价于上面/* 中间异常退出智能指针也会自动释放资源。if (xxx) {throw ....;}if (yyy) {return -1;}*/
} 3. 智能指针分类
3.1 问题引入 接着使用上述自己实现的智能指针进行拷贝构造
void test02() {MySmartPtrint p1(new int); // p1指向一块int型内存空间MySmartPtrint p2(p1); // p2指向p1指向的内存空间*p1 10; // 内存空间的值为10*p2 20; // 内存空间的值被改为20
}
但运行时出错 原因在于p1和p2指向同一块int型堆区内存空间p2析构将该int型空间释放p1再析构时释放同一块内存则出错。
那可否使用如下深拷贝解决该问题
MySmartPtr(cosnt MySmartPtrT src) {mptr new T(*src.mptr);
}
不可以。因为按照裸指针的使用方式用户本意是想将p1和p2都指向该int型堆区内存使用指针p1、p2都可改变该内存空间的值显然深拷贝不符合此场景。 3.2 两类智能指针
不带引用计数的智能指针只能有一个指针管理资源。 auto_ptr scoped_ptr unique_ptr.
带引用计数的智能指针可以有多个指针同时管理资源。 shared_ptr强智能指针。 weak_ptr: 弱智能指针。这是特例不能控制资源的生命周期不能控制资源的自动释放 3.3 不带引用计数的智能指针
只能有一个指针管理资源。
3.3.1 auto_ptr 不推荐使用
void test03() {auto_ptrint ptr1(new int);auto_ptrint ptr2(ptr1);*ptr2 20;// cout *ptr2 endl; // 可访问*ptr2cout *ptr1 endl; //访问*ptr1却报错
}
如上代码访问*ptr1为何报错
因为调用auto_ptr的拷贝构造将ptr1的值赋值给ptr2后底层会将ptr1指向nullptr即将同一个指针拷贝构造多次时只让最后一次拷贝的指针管理资源前面的指针全指向nullptr。
不推荐将auto_ptr存入容器。
3.3.2 scoped_ptr 使用较少
scoped_ptr已将拷贝构造函数和赋值运算符重载delete了。
scoped_ptr(const scoped_ptrT) delete; // 删除拷贝构造
scoped_ptrT operator(const scoped_ptrT) delete; // 删除赋值重载
3.3.3 unique_ptr 推荐使用
unique_ptr也已将拷贝构造函数和赋值运算符重载delete。
unique_ptr(const unique_ptrT) delete; // 删除拷贝构造
unique_ptrT operator(const unique_ptrT) delete; // 删除赋值重载
但unique_ptr提供了带右值引用参数的拷贝构造函数和赋值运算符重载如下
void test04() {unique_ptrint ptr1(new int);// unique_ptrint ptr2(ptr1); 和scoped_ptr一样无法通过编译unique_ptrint ptr2(std::move(ptr1)); // 但可使用move得到ptr1的右值类型// *ptr1 也无法访问
} 3.4 带引用计数的智能指针
可以有多个指针同时管理资源。
原理给智能指针添加其指向资源的引用计数属性若引用计数 0则不会释放资源若引用计数 0就释放资源。
具体来说额外创建资源引用计数类在智能指针类中加入该资源引用计数类的指针作为其中的一个属性当使用裸指针创建智能指针对象时创建智能指针中的资源引用计数对象并将其中的引用计数属性初始化为1当后面对该智能指针对象进行拷贝使用其他智能指针指向该资源时或时需要在其他智能指针对象类中将被拷贝的智能指针对象中的资源引用计数类的指针获取过来然后将引用计数1当用该智能指针给其他智能指针进行赋值时因为其他智能指针被赋值后它们就不指向原先的资源了原先资源的引用计数就-1直至引用计数为0时delete掉资源当智能指针对象析构时会使用其中的资源引用计数指针将共享的引用计数-1直至引用计数为0时delete掉资源。
shared_ptr强智能指针可改变资源的引用计数。
weak_ptr弱智能指针不可改变资源的引用计数。
带引用计数的智能指针的简单实现
/*资源的引用计数类*/
templatetypename T
class RefCnt {
public:RefCnt(T* ptrnullptr):mptr(ptr) {if (mptr ! nullptr) {mcount 1; // 刚创建指针指针时引用计数初始化为1}}void addRef() { // 增加引用计数mcount;}int delRef() { // 减少引用计数mcount--;return mcount;}
private:T* mptr; // 资源地址int mcount; // 资源的引用计数
};/*智能指针类*/
templatetypename T
class MySmartPtr {
public:MySmartPtr(T* ptr nullptr) :mptr(ptr) { // 创建该对象时裸指针会传给对象mpRefCnt new RefCntT(mptr);}~MySmartPtr() { // 对象出作用域会自动析构因此会释放裸指针指向的资源if (0 mpRefCnt-delRef()) {delete mptr;mptr nullptr;}}// *运算符重载T operator*() { // 提供智能指针的解引用操作即返回它包装的裸指针的解引用return *mptr;}// -运算符重载T* operator-() { // 即返回裸指针return mptr;}// 拷贝构造MySmartPtr(const MySmartPtrT src):mptr(src.mptr),mpRefCnt(src.mpRefCnt) {if (mptr ! nullptr) {mpRefCnt-addRef();}}// 赋值重载MySmartPtrT operator(const MySmartPtrT src) {if (this src) // 防止自赋值return *this;/*若本指针改为指向src管理的资源则本指针原先指向的资源的引用计数-1若原资源的引用计数为0就释放资源*/if (0 mpRefCnt-delRef()) { delete mptr;}mptr src.mptr;mpRefCnt src.mpRefCnt;mpRefCnt-addRef();return *this;}
private:T* mptr; // 指向资源的指针RefCntT* mpRefCnt; // 资源的引用计数
};
3.4.1 shared_ptr
强智能指针。可改变资源的引用计数。
1强智能指针的交叉引用问题
class B;class A {
public:A() {cout A() endl;}~A() {cout ~A() endl;}shared_ptrB _ptrb;
};class B {
public:B() {cout B() endl;}~B() {cout ~B() endl;}shared_ptrA _ptra;
};void test06() {shared_ptrA pa(new A());shared_ptrB pb(new B());pa-_ptrb pb;pb-_ptra pa;/*打印pa、pb指向资源的引用计数*/cout pa.use_count() endl;cout pb.use_count() endl;
}
输出结果 可见pa、pb指向的资源的引用计数都为2因此出了作用域导致pa、pb指向的资源都无法释放如下图所示 解决
建议定义对象时使用强智能指针引用对象时使用弱智能指针防止出现交叉引用的问题。
什么是定义对象什么是引用对象
定义对象 使用new创建对象并创建一个新的智能指针管理它。
引用对象 使用一个已存在的智能指针来创建一个新的智能指针。 定义对象和引用对象的示例如下
shared_ptrint p1(new int()); // 定义智能指针对象p1
shared_ptrint p2 make_sharedint(10); // 定义智能指针对象p2shared_ptrint p3 p1; // 引用智能指针p1并使用p3来共享它
weak_ptrint p4 p2; // 引用智能指针p2并使用p4来观察它
如上述代码因为在test06函数中使用pa对象的_ptrb引用pb对象使用pb对象的_ptra引用pa对象因此需要将A类、B类中的_ptrb和_ptra的类型改为弱智能指针weak_ptr即可这样就不会改变资源的引用计数能够正确释放资源。
3.4.2 weak_ptr
弱智能指针。不能改变资源的引用计数、不能管理对象生命周期、不能做到资源自动释放、不能创建对象也不能访问资源因为weak_ptr未提供operator-和operator*运算符重载即不能通过弱智能指针调用函数、不能将其解引用。只能从一个已有的shared_ptr或weak_ptr获得资源的弱引用。
弱智能指针weak_ptr若想用访问资源则需要使用lock方法将其提升为一个强智能指针提升失败则返回nullptr。提升的情形常使用于多线程环境避免无效的访问提升程序安全性
注意弱智能指针weak_ptr只能观察资源的状态但不能管理资源的生命周期不会改变资源的引用计数不能控制资源的释放。
weak_ptr示例
void test07() {shared_ptrBoy boy_sptr(new Boy());weak_ptrBoy boy_wptr(boy_sptr);// boy_wptr-study(); 错误无法使用弱智能指针访问资源cout boy_sptr.use_count() endl; // 引用计数为1因为弱智能指针不改变引用计数shared_ptrint i_sptr(new int(99));weak_ptrint i_wptr(i_sptr);// cout *i_wptr endl; 错误无法使用弱智能指针访问资源cout i_sptr.use_count() endl; // 引用计数为1因为弱智能指针不改变引用计数/*弱智能指针提升为强智能指针*/shared_ptrBoy boy_sptr1 boy_wptr.lock();if (boy_sptr1 ! nullptr) {cout boy_sptr1.use_count() endl; // 提升成功引用计数为2boy_sptr1-study(); // 可以调用}shared_ptrint i_sptr1 i_wptr.lock();if (i_sptr1 ! nullptr) {cout i_sptr1.use_count() endl; // 提升成功引用计数为2cout *i_sptr1 endl; // 可以输出}
} 4. 智能指针与多线程访问共享资源的安全问题
现要实现主线程创建子线程让子线程执行打印Hello的函数有如下两种方式
方式1主线程调用test08函数在test08函数中启动子线程执行线程函数如下
void handler() {cout Hello endl;
}void func() {thread t1(handler);
}int main(int argc, char** argv) {func();this_thread::sleep_for(chrono::seconds(1));system(pause);return 0;
}
运行报错 方式2主线程中直接创建子线程来执行线程函数如下
void handler() {cout Hello endl;
}int main(int argc, char** argv) {thread t1(handler);this_thread::sleep_for(chrono::seconds(1));system(pause);return 0;
}
运行结果无报错 上面两种方式都旨在通过子线程调用函数输出Hello但为什么方式1报错很简单不再赘述。 回归本节标题的正题有如下程序
class C {
public:C() {cout C() endl;}~C() {cout ~C() endl;}void funcC() {cout C::funcC() endl;}
private:};/*子线程执行函数*/
void threadHandler(C* c) {this_thread::sleep_for(chrono::seconds(1));c-funcC();
}/* 主线程 */
int main(int argc, char** argv) {C* c new C();thread t1(threadHandler, c);delete c;t1.join();return 0;
}
运行结果 结果显示c指向的对象被析构了但是仍然使用该被析构的对象调用了其中的funcC函数显然不合理。
因此在线程函数中使用c指针访问A对象时需要观察A对象是否存活。
使用弱智能指针weak_ptr接收对象访问对象之前尝试提升为强智能指针shared_ptr提升成功则访问否则对象被析构。
情形1对象被访问之前就被析构了
class C {
public:C() {cout C() endl;}~C() {cout ~C() endl;}void funcC() {cout C::funcC() endl;}
private:};/*子线程执行函数*/
void threadHandler(weak_ptrC pw) { // 引用时使用弱智能指针this_thread::sleep_for(chrono::seconds(1));shared_ptrC ps pw.lock(); // 尝试提升if (ps ! nullptr) {ps-funcC();} else {cout 对象已经析构 endl;}
}/* 主线程 */
int main(int argc, char** argv) {{shared_ptrC p(new C());thread t1(threadHandler, weak_ptrC(p));t1.detach();}this_thread::sleep_for(chrono::seconds(5));return 0;
}运行结果 情形2 对象访问完才被析构
class C {
public:C() {cout C() endl;}~C() {cout ~C() endl;}void funcC() {cout C::funcC() endl;}
private:};/*子线程执行函数*/
void threadHandler(weak_ptrC pw) { // 引用时使用弱智能指针this_thread::sleep_for(chrono::seconds(1));shared_ptrC ps pw.lock(); // 尝试提升if (ps ! nullptr) {ps-funcC();} else {cout 对象已经析构 endl;}
}/* 主线程 */
int main(int argc, char** argv) {{shared_ptrC p(new C());thread t1(threadHandler, weak_ptrC(p));t1.detach();this_thread::sleep_for(chrono::seconds(5));} return 0;
}
运行结果 可见shared_ptr与weak_ptr结合使用能够较好地保证多线程访问共享资源的安全。 5.智能指针的删除器deleter
删除器是智能指针释放资源的方式默认使用操作符delete来释放资源。
但并非所有智能指针管理的资源都可通过delete释放如数组、文件资源、数据库连接资源等。
有如下智能指针对象管理一个数组资源
unique_ptrint ptr1(new int[100]);
此时再用默认的删除器则会造成资源泄露因此需要自定义删除器。
一些为部分自定义删除器的示例
/* 方式1类模板 */
templatetypename T
class MyDeleter {
public:void operator()(T* ptr) const {cout 数组自定义删除器1. endl;delete[] ptr;}
};/* 方式2函数 */
void myDeleter(int* p) {cout 数组自定义删除器2. endl;delete[] p;
}void test09() {unique_ptrint, MyDeleterint ptr1(new int[100]);unique_ptrint, void(*)(int*) ptr2(new int[100], myDeleter);/* 方式3Lambda表达式 */unique_ptrint, void(*)(int*) ptr3(new int[100], [](int* p) {cout 数组自定义删除器3. endl;delete[] p;});
}void test10() {unique_ptrFILE, void(*)(FILE*) ptr2(fopen(1.txt, w), [](FILE* f) {cout 文件自定义删除器. endl;fclose(f);});
}
运行结果 待补充 文章转载自: http://www.morning.grxyx.cn.gov.cn.grxyx.cn http://www.morning.xbptx.cn.gov.cn.xbptx.cn http://www.morning.yqhdy.cn.gov.cn.yqhdy.cn http://www.morning.yfpnl.cn.gov.cn.yfpnl.cn http://www.morning.thwcg.cn.gov.cn.thwcg.cn http://www.morning.tzkrh.cn.gov.cn.tzkrh.cn http://www.morning.yhywr.cn.gov.cn.yhywr.cn http://www.morning.dywgl.cn.gov.cn.dywgl.cn http://www.morning.gcqdp.cn.gov.cn.gcqdp.cn http://www.morning.kfsfm.cn.gov.cn.kfsfm.cn http://www.morning.jqkjr.cn.gov.cn.jqkjr.cn http://www.morning.wanjia-sd.com.gov.cn.wanjia-sd.com http://www.morning.zpdjh.cn.gov.cn.zpdjh.cn http://www.morning.ddgl.com.cn.gov.cn.ddgl.com.cn http://www.morning.chhhq.cn.gov.cn.chhhq.cn http://www.morning.ccjhr.cn.gov.cn.ccjhr.cn http://www.morning.pctsq.cn.gov.cn.pctsq.cn http://www.morning.kwrzg.cn.gov.cn.kwrzg.cn http://www.morning.gprzp.cn.gov.cn.gprzp.cn http://www.morning.ckctj.cn.gov.cn.ckctj.cn http://www.morning.krzrg.cn.gov.cn.krzrg.cn http://www.morning.wmyqw.com.gov.cn.wmyqw.com http://www.morning.jpdbj.cn.gov.cn.jpdbj.cn http://www.morning.xqjz.cn.gov.cn.xqjz.cn http://www.morning.yrnyz.cn.gov.cn.yrnyz.cn http://www.morning.knzdt.cn.gov.cn.knzdt.cn http://www.morning.hdnd.cn.gov.cn.hdnd.cn http://www.morning.crdtx.cn.gov.cn.crdtx.cn http://www.morning.txzqf.cn.gov.cn.txzqf.cn http://www.morning.nwzcf.cn.gov.cn.nwzcf.cn http://www.morning.hdwjb.cn.gov.cn.hdwjb.cn http://www.morning.ryywf.cn.gov.cn.ryywf.cn http://www.morning.wqbzt.cn.gov.cn.wqbzt.cn http://www.morning.tdxnz.cn.gov.cn.tdxnz.cn http://www.morning.bgpch.cn.gov.cn.bgpch.cn http://www.morning.xzlp.cn.gov.cn.xzlp.cn http://www.morning.wrcgy.cn.gov.cn.wrcgy.cn http://www.morning.qxlxs.cn.gov.cn.qxlxs.cn http://www.morning.qqtzn.cn.gov.cn.qqtzn.cn http://www.morning.lzbut.cn.gov.cn.lzbut.cn http://www.morning.cbczs.cn.gov.cn.cbczs.cn http://www.morning.psyrz.cn.gov.cn.psyrz.cn http://www.morning.xnzmc.cn.gov.cn.xnzmc.cn http://www.morning.krdmn.cn.gov.cn.krdmn.cn http://www.morning.zztkt.cn.gov.cn.zztkt.cn http://www.morning.rtlrz.cn.gov.cn.rtlrz.cn http://www.morning.xxgfl.cn.gov.cn.xxgfl.cn http://www.morning.dzfwb.cn.gov.cn.dzfwb.cn http://www.morning.gyqnp.cn.gov.cn.gyqnp.cn http://www.morning.zbpqq.cn.gov.cn.zbpqq.cn http://www.morning.smpb.cn.gov.cn.smpb.cn http://www.morning.sqqkr.cn.gov.cn.sqqkr.cn http://www.morning.rmfh.cn.gov.cn.rmfh.cn http://www.morning.cgntj.cn.gov.cn.cgntj.cn http://www.morning.pmdnx.cn.gov.cn.pmdnx.cn http://www.morning.srbmc.cn.gov.cn.srbmc.cn http://www.morning.qswws.cn.gov.cn.qswws.cn http://www.morning.yktwr.cn.gov.cn.yktwr.cn http://www.morning.rnxs.cn.gov.cn.rnxs.cn http://www.morning.epeij.cn.gov.cn.epeij.cn http://www.morning.rlbfp.cn.gov.cn.rlbfp.cn http://www.morning.yldgw.cn.gov.cn.yldgw.cn http://www.morning.gwgjl.cn.gov.cn.gwgjl.cn http://www.morning.trtdg.cn.gov.cn.trtdg.cn http://www.morning.qgtfl.cn.gov.cn.qgtfl.cn http://www.morning.stsnf.cn.gov.cn.stsnf.cn http://www.morning.zztmk.cn.gov.cn.zztmk.cn http://www.morning.tzlfc.cn.gov.cn.tzlfc.cn http://www.morning.wmfh.cn.gov.cn.wmfh.cn http://www.morning.bbgr.cn.gov.cn.bbgr.cn http://www.morning.syxmx.cn.gov.cn.syxmx.cn http://www.morning.lskrg.cn.gov.cn.lskrg.cn http://www.morning.fkmrj.cn.gov.cn.fkmrj.cn http://www.morning.rxkl.cn.gov.cn.rxkl.cn http://www.morning.yxbrn.cn.gov.cn.yxbrn.cn http://www.morning.ltywr.cn.gov.cn.ltywr.cn http://www.morning.ymyhg.cn.gov.cn.ymyhg.cn http://www.morning.rqzyz.cn.gov.cn.rqzyz.cn http://www.morning.ryjl.cn.gov.cn.ryjl.cn http://www.morning.thrcj.cn.gov.cn.thrcj.cn