赞
踩
RAII(Resource Acquisition Is Initialization ,资源获得即初始化),是一种利用对象生命周期来控制程序资源的简单技术。在对象构造时获得资源,接着控制对资源的访问在对象的生命周期内始终有效,最后在对象析构时释放资源。因为C++的语言机制保证了,当一个对象创建的时候,自动调用构造函数,当对象超出作用域的时候会自动调用析构函数。所以,在RAII的指导下,我们应该使用类来管理资源,将资源和对象的生命周期绑定。这样的好处在于:
智能指针包含在头文件 < memory >中,标准命名std空间下,有auto_ptr(已经被弃用)、shared_ptr、weak_ptr、unique_ptr。智能指针就是模拟指针动作的类,一般智能指针都会重载 **-> **和 *** **,主要作用时管理动态内存的释放。
智能指针是一个类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源。
处理资源泄漏;有指针的时候,忘记释放已经不使用的内存,从而导致内存泄漏;
处理空悬指针;虽然释放了申请的内存,但是ptr会变成空悬指针(野指针),会指向垃圾内存,因此需要将内存释放后的指针置空(ptr = nullptr;)
比较隐晦的由异常造成的资源泄漏;new创建对象后因为发生异常而忘记调用delete。
多个指针指向相同的对象,采用引用计数的方式解决赋值与拷贝问题,每个shared_ptr的拷贝都指向同一块内存,每次拷贝内部引用计数+1,每次析构内部引用计数-1,为0时自动删除所指向的堆内存。其内部的引用计数是线程安全的,但是对象读取时需要加锁。
智能指针对象中引用计数是多个智能指针对象共享的,两个线程中的引用计数同时++或者–,这个操作不是原子的,举个例子,应用计数原来是1,++了两次,可能还是2。这样引用计数就错乱了,会导致资源未释放或者程序崩溃的问题,所以其实智能指针是加锁了的,也就是说引用计数的操作是线程安全的。
声明: template < class T > class shared_ptr;
template <typename T> class sharedPointer { private: class Implement { public: Implement(T* p) : mPointer(p), mRefs(1) {} ~Implement() { delete mPointer; } T* mPointer; size_t mRefs; }; Implement* mImplPtr; public: explicit sharedPointer(T *p) : mImplPtr (new Implement(p)) {} //explicit不能发生隐式类型转换,就是参数类型不匹配,就会进行隐式转换 ~sharedPointer() { decrease(); } //计数递减 sharedPointer(const sharedPointer& other) : mImplPtr(other.mImplPtr) { increase(); } //计数递增 sharedPointer operator = (const sharedPointer& other) { if (mImplPtr != other.mImplPtr) //避免自赋值 { decrease(); mImplPtr = other.mImplPtr; increase(); } return *this; } T* operator -> () const { return mImplPtr->mPointer; } T* operator *() const { return *(mImplPtr->mPointer); } private: void decrease() { if (--mImplPtr->mRefs) == 0) delete mImplPtr; } void increase() { ++(mImplPtr->mRefs); } };
**make_shared:**是一种安全分配和使用动态内存的方法,主要功能是在动态内存中分配一个对象并且使用它,返回此对象的shared_ptr。
make_shared生成shared_ptr的优点和缺点:
1)效率更高,原始的 new 表达式分配对象, 然后传递给 shared_ptr (也就是使用 shared_ptr 的构造函数) 的话, shared_ptr 的实现没有办法选择, 而只能单独的分配控制块,但是make_shared内存分配,可以一次性完成,减少了内存分配的次数,所以效率高;
auto p = new widget();
shared_ptr sp1{ p }, sp2{ sp1 };
auto sp1 = make_shared(), sp2{ sp1 };
2)异常安全。比如说可能连续构造两个对象,然后再分配shared_ptr,那我构造一个对象发生可能异常,这个对象就泄漏了,就是说有可能造成shared_ptr 没有立即获得裸指针。这种情况就推荐使用make_shared来代替。
void F(const std::shared_ptr<Lhs>& lhs, const std::shared_ptr<Rhs>& rhs) { /* ... */ }
F(std::shared_ptr<Lhs>(new Lhs("foo")),std::shared_ptr<Rhs>(new Rhs("bar")));
F(std::make_shared<Lhs>("foo"), std::make_shared<Rhs>("bar"));
3)构造函数是保护或者私有的,不能使用make_shared
4)make_shared对象的内存可能没有办法及时回收。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。