C++智能指针

智能指针

包含于头文件 #include <memory>

  • 内存泄漏

    • 申请的内存忘记释放

    • 申请的动态内存无法执行delete/free

      class A{
      public:
          A(int n=0){
              p = new int(n);
              cout << "A构造" << endl;
          }
          ~A(){
              cout << "A析构" << endl;
              delete p;
          }
      private:
         	int *p;
      };
      
      void func(){
          A *pa = new A(1024);//构造    
          
          //如果在delete之前发生异常
          new int[0xFFFFFFFF];// throw string("产生异常");  -->直接跳出  try -- catch
          //下面的语句可能无法被执行  造成了delete pa没有执行,申请的动态内存没有释放  内存泄露
          
          delete pa;  //析构
      }
      
      
  • 智能指针的意义:

    • 管理动态内存,避免内存泄漏
    • 不用手动delete
  • 原理:智能指针创建的是局部对象

    • 在异常机制里,局部对象能正常析构,即如果产生异常,一定会调用智能指针所声名类的析构函数,能delete释放申请的动态内存。
    • setjmplongjmp在C++中,局部对象无法得到析构(在调用longjmp函数之前的局部对象)产生内存泄露
  • 智能指针只能管理new出来的动态内存,不能用非动态内存构造智能指针,对导致程序崩溃,因为智能指针的析构函数中,对内存使用了delete操作。

  • 用智能指针管理动态内存时, 不能再手动delete,会报错==double free==

  • 用处

    • 它可用于为动态分配的对象提供异常安全(产生异常时,动态内存能够得到析构)、传递动态分配对象的所有权给函数和从函数返回动态分配的对象。
auto_ptr

C++17被删除

  • C++11前只有auto_ptr,,C++11添加unique_ptr,shared_ptr,weak_ptr来取代auto_ptr,C++17中,auto_ptr就被删除了

  • auto_ptr 是拥有严格对象所有权语义的智能指针

  • 被删除的原因:

    • auto_ptr的拷贝构造和拷贝赋值的实现是以移动构造和移动赋值的语义实现的(C++11之前没有移动构造和移动赋值)

    • 复制 auto_ptr ,会复制指针并转移所有权给目标: auto_ptr 的复制构造和复制赋值都会修改其右侧参数,而且“副本”不等于原值。因为这些不常见的复制语义,不可将 auto_ptr 置于标准容器中。此用途及其他使用更适合用 unique_ptr

    • 即拷贝构造之后,源对象的控制权被交给了新的智能指针对象,所以原智能指针就无法使用源对象

      auto_ptr<int> p1(new int(1024));
      auto_ptr<int > p2(p1);//p1失效了
      
    • 拷贝赋值也意味着控制权的转移和释放

      auto_ptr<int> p1(new int(1024));
      auto_ptr<int> p2(new int(1024));
      p2 = p1;//p就失效了   p1原来的控制的动态内存会delete,转而控制原来p控制的动态内存
      
    • 构造函数

      explicit auto_ptr( X* p = 0 ) throw();  //参数必须是new出来的动态内存
      auto_ptr( auto_ptr& r ) throw();        //拷贝构造实现的却是移动构造
      auto_ptr& operator=(auto_ptr& r);       //拷贝赋值实现的却是移动构造
      
    • 观察器

      T* get() const throw();
      
      //智能指针在使用时,可以像指针一样使用     解引用    ->
      T& operator*() const throw();
      T* operator->() const throw(); 
      
    • 修改器

      //释放被管理对象的所有权
      T* release() throw();//返回保有的指针。调用后 *this 保有空指针。
      
      void reset( T* p = 0 ) throw();
      
    • 同一块动态内存不能用多个智能指针对象管理

      int *p = new int(1024);
      //不管什么智能指针  都不能这样干!!!!!
      auto_ptr<int> app1(p);  //app1消亡时 delete p;
      auto_ptr<int> app2(p);  //app2消亡时 delete p;
      //double free 
      
    • auto_ptr 不支持完整语义上的拷贝,不能放入容器

    • auto_ptr任意时刻只有一智能指针拥有对象的控制权

    • 11之后就用unique_ptr

unique_ptr
template<
    class T,
    class Deleter = std::default_delete<T>
> class unique_ptr;

//针对数组类型进行了偏特化(局部特化)
template <
    class T,
    class Deleter
> class unique_ptr<T[], Deleter>;
  • std::unique_ptr 是通过指针占有并管理另一对象,并在 unique_ptr 离开作用域时释放该对象的智能指针。

    在下列两者之一发生时用关联的删除器释放对象:

    • 销毁了管理的 unique_ptr 对象
    • 通过 operator= 或 reset() 赋值另一指针给管理的 unique_ptr 对象。
  • unique_ptr删除了(不支持)拷贝构造和拷贝赋值,实现了(只支持)移动构造和移动赋值,确保了此指针为独有所有权的智能指针

  • 不支持拷贝构造和拷贝赋值,只支持移动,所以就确保了 拥有独有对象所有权语义的智能指针

    explicit operator bool() const noexcept;
    //检查 *this 是否占有对象,即是否有 get() != nullptr 
    
    typename std::add_lvalue_reference<T>::type operator*() const;
    pointer operator->() const noexcept; 
    
    //针对数组特化版本
    T& operator[]( std::size_t i ) const;
    
shared_ptr

共享智能指针

template< class T > class shared_ptr;
  • std::shared_ptr 是通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可占有同一对象。

  • 下列情况之一出现时销毁对象并解分配其内存

    • 最后剩下的占有对象的 shared_ptr 被销毁;
    • 最后剩下的占有对象的 shared_ptr 被通过 operator=reset() 赋值为另一指针。
  • 引用计数

    • 调用构造函数创建的shared_ptr,引用计数为1
    • 通过拷贝构造创建的,引用计数+1
    • shared_ptr消亡时,引用计数-1
  • 构造函数

    constexpr shared_ptr() noexcept;
    constexpr shared_ptr( std::nullptr_t ) noexcept; 
    
    explicit shared_ptr( Y* ptr );
    shared_ptr( Y* ptr, Deleter d );
    
    //支持拷贝构造  和  移动 构造
    shared_ptr( const shared_ptr<Y>& r ) ;
    shared_ptr( shared_ptr<Y>&& r ) noexcept;
    
  • 赋值

    shared_ptr& shared_ptr( const shared_ptr<Y>& r ) ;
    shared_ptr& shared_ptr(shared_ptr<Y>&& r ) ;
    
  • 观察器

    void reset() noexcept;
    void reset( Y* ptr );
    void reset( Y* ptr, Deleter d );
    
    void swap( shared_ptr& r ) noexcept;
    
  • 对于shared_ptr管理数组的动态内存,需要在构造时 指定deleter

    T* get() const noexcept;
    element_type* get() const noexcept; 
    
    T& operator*() const noexcept;
    T* operator->() const noexcept; 
    
    element_type& operator[]( std::ptrdiff_t idx ) const;
    //引用计数
    long use_count() const noexcept;
    
    bool unique() const noexcept;//use_count() == 1
    
    explicit operator bool() const noexcept;
    
    bool owner_before( const shared_ptr<Y>& other) const noexcept;
    bool owner_before( const std::weak_ptr<Y>& other) const noexcept;
    
  • 非成员方法

    shared_ptr<T> make_shared( Args&&... args );
    
  • 对于shared_ptr管理数组的动态内存,需要在构造时指定deleter

    class Del{
    public:
        void operator()(const A* ptr)const{
            delete [] ptr;
        }
    };
    
    shared_ptr<A> sp(new A[4],Del());
    shared_ptr<A> sp1(new A[4],default_delete<A[]>());
    
weak_ptr
  • 解决了shared_ptr循环引用导致的内存泄漏的问题

    • weak_ptr是用shared_ptr进行构造和赋值
    struct B;
    struct A{
        A(){cout << "A()" << endl;}
    	~A(){cout << "~A()" << endl;}
        shared_ptr<B> sp;
        //weak_ptr<B> sp;
    };
    struct B{
      	B(){cout << "B()" << endl;}
        ~B(){cout << "~B()" << endl;}
        shared_ptr<A> sp;
        //weak_ptr<A> sp;
    };
    
    //循环引用
    shared_ptr<A> pa(new A());
    shared_ptr<B> pb(new B());
    pa->sp = pb;  //weak_ptr 用 shared_ptr进行构造和赋值
    pb->sp = pa;
    
    //此时不会调用A 和B的析构函数
    //替换为弱指针后可正常析构
    
  • 通过shared_ptr构造weak_ptr不会引发引用计数+1

  • std::weak_ptr 是一种智能指针,它对被 shared_ptr 管理的对象存在非拥有性(「弱」)引用。在访问所引用的对象前必须先转换为 shared_ptr。

std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性(「弱」)引用。在访问所引用的对象前必须先转换为 std::shared_ptr

constexpr weak_ptr() noexcept;

weak_ptr( const weak_ptr& r ) noexcept;

template< class Y >
weak_ptr( const weak_ptr<Y>& r ) noexcept;

template< class Y >
weak_ptr( const std::shared_ptr<Y>& r ) noexcept;  //不会使得引用计数+1

weak_ptr( weak_ptr&& r ) noexcept;

template< class Y >
weak_ptr( weak_ptr<Y>&& r ) noexcept 

    
void reset() noexcept; 
void swap( weak_ptr& r ) noexcept;

long use_count() const noexcept;//返回共享被管理对象所有权的 shared_ptr 实例数量,或0,若被管理对象已被删除,即 *this 为空。

bool expired() const noexcept;//等价于 use_count() == 0 。可能仍未对被管理对象调用析构函数,但此对象的析构已经临近(或可能已发生)。

std::shared_ptr<T> lock() const noexcept;
//创建新的 std::shared_ptr 对象,它共享被管理对象的所有权。若无被管理对象,即 *this 为空,则返回亦为空的 shared_ptr 。等效地返回 expired() ? shared_ptr<T>() : shared_ptr<T>(*this) ,原子地执行。
各个智能指针的特点(还存在的)
unique_ptrshared_ptrweak_ptr
克服了auto_ptr拷贝语义使用移动赋值和移动构造的缺点,不允许拷贝构造和拷贝赋值,仅提供移动构造和移动赋值。可以由多个智能指针共享同一个动态内存的管理权限,对一个指针进行操作时。解决了shared_ptr循环引用导致的内存泄漏的问题

两个智能的简单实现:

weak_ptr实现以及简单测试

#include <iostream>
using namespace std;
class Counter{
public:
    int scnt;
    int wcnt;
public:
    Counter(int scnt = 1, int wcnt = 0):scnt(scnt), wcnt(wcnt){
		cout << "Counter()" << endl;
	}
	~Counter(){
		cout << "~Counter()" << endl;
	}

};

template<class T>
class Weakptr;

template<class T>
class SharedPtr{
private:
    T* ptr;
    Counter *pct;
public:
    SharedPtr(T* ptr = nullptr):ptr(ptr){
        if(ptr != nullptr){
            pct = new Counter(1, 0);
        }
        else{
            pct = nullptr;
        }
    }
    void reset(void){
        if(pct != nullptr && ptr != nullptr){
            --(pct->scnt);
            if(pct->scnt == 0){
                delete ptr;
                if(pct->wcnt == 0){
                    delete pct;
                }
            }
        }
        pct = nullptr;
        ptr = nullptr;
    }
    void reset(T* ptr){
        reset();
        this->ptr = ptr;
        if(ptr != nullptr){
            pct = new Counter(1,0);
        }
    }
    ~SharedPtr(void){
        reset();
    }
    int use_count()const{
        return pct == nullptr?0:pct->scnt;
    }
    SharedPtr(const SharedPtr& sp){
        ptr = sp.ptr;
        pct = sp.pct;
        if(pct != nullptr){
            ++pct->scnt;
        }
    }
    SharedPtr(SharedPtr&& sp){
        ptr = sp.ptr;
        pct = sp.pct;
        sp.pct = nullptr;
        sp.ptr = nullptr;
    }
    SharedPtr& operator=(const SharedPtr& sp){
        if(ptr != sp.ptr){
            reset();
            ptr = sp.ptr;
            pct = sp.pct;
            if(pct != nullptr){
                ++pct->scnt;
            }
        }
        return *this;
    }
    SharedPtr& operator=(SharedPtr&& sp){
        if(ptr != sp.ptr){
            reset();
            ptr = sp.ptr;
            pct = sp.pct;
            sp.pct = nullptr;
            sp.ptr = nullptr;
        }
        return *this;
    }
    T* get(){
        return ptr;
    }
    T& operator*(void){
        return *ptr;
    }
    T* operator->(void){
        return ptr;
    }
    operator bool(void){
        return ptr != nullptr;
    }
    friend class Weakptr<T>;
};

template<class T>
class Weakptr{
private:
    T* ptr;
    Counter *pct;
public:
    Weakptr(){
        ptr = nullptr;
        pct = nullptr;
    }
    Weakptr(SharedPtr<T> sp){
        ptr = sp.ptr;
        pct = sp.pct;
        if(pct != nullptr){
            ++pct->wcnt;
        }
    }
    void reset(){
        if(pct != nullptr){
            --pct->wcnt;
            if(pct->scnt == 0 && pct->wcnt == 0){
                delete pct;
            }
        }
        ptr = nullptr;
        pct = nullptr;
    }
    ~Weakptr(void){
        reset();
    }
    Weakptr(const Weakptr& wp){
        ptr = wp.ptr;
        pct = wp.pct;
        if(pct != nullptr){
            ++pct->wcnt;
        }
    }
    Weakptr(Weakptr&& wp){
        ptr = wp.ptr;
        pct = wp.pct;
        wp.pct = nullptr;
        wp.ptr = nullptr;
    }
    Weakptr& operator=(const SharedPtr<T>& sp){
        reset();
        ptr = sp.ptr;
        pct = sp.pct;
        if(pct != nullptr){
            ++pct->wcnt;
        }
        return *this;
    }
    Weakptr& operator=(const Weakptr& wp){
        if(pct != wp.pct){
            reset();
            ptr = wp.ptr;
            pct = wp.pct;
            if(pct != nullptr)
                pct->wcnt++;
        }
        return *this;
    }
    Weakptr& operator=(Weakptr&& wp){
        if(pct != wp.pct){
            reset();
            ptr = wp.ptr;
            pct = wp.pct;
            wp.pct = nullptr;
            wp.ptr = nullptr;
        }
        return *this;
    }
    size_t use_count(){
        return pct == nullptr?0:pct->scnt;
    }
    bool expired(){
        return pct == nullptr||pct->scnt == 0;
    }
    SharedPtr<T> lock(){
        SharedPtr<T> sp;
        sp.ptr = ptr;
        sp.pct = pct;
        if(pct != nullptr){
            ++pct->scnt;
        }
        return sp;
    }
};
struct A{
	int x;
	int y;
	A(int x=0,int y=0):x(x),y(y){
		cout << "A()" << endl;
	}

	~A(){
		cout << "~A()" << endl;
	}

	void func(){
		cout << "(" << x << "," << y << ")" << endl;
	}
};



int main(int argc,char *argv[]){
	Weakptr<A> wp;
	{
		SharedPtr<A> sp(new A(1024,9527));
		wp = sp;
		cout << wp.use_count() << endl;
		SharedPtr<A> sp1 = wp.lock();
		cout << sp1.use_count() << endl;
	}
	cout << wp.use_count() << endl;
	

    return 0;
}

unique_ptr

#include <iostream>
using namespace std;
template<typename T>
class Mydel{
public:
	void operator()(T *ptr)const{
		cout << "delete ptr" << endl;
		delete ptr;
	}
};

template<typename T>
class Mydel<T[]>{
public:
	void operator()(T* ptr)const{
		cout << "delete [] ptr" << endl;
		delete [] ptr;
	}
};


template<class T,class Deleter=Mydel<T> >
class UniquePtr{
public:
	UniquePtr( T* ptr=nullptr):ptr(ptr){
	
	}
	//不支持拷贝构造 和 拷贝赋值
	UniquePtr(const UniquePtr& p) = delete;
	UniquePtr& operator=(const UniquePtr& p) = delete;
	
	UniquePtr(UniquePtr&& p):ptr(p.ptr),del(p.del){
		p.ptr = nullptr;
	}

	UniquePtr& operator=(UniquePtr&& p){
		if(&p != this){
			if(ptr!=nullptr){
				del(ptr);
			}
			ptr = p.ptr;
			del = p.del;
			p.ptr = nullptr;
		}
		return *this;
	}
	
	~UniquePtr(void){
		if(ptr!=nullptr){
			del(ptr);
		}
	}

	T* release(void){
		T * retptr = ptr;
		ptr = nullptr;
		return retptr;
	}

	T* get(void)const{
		return ptr;
	}

	void reset(T *ptr){
		if(this->ptr != nullptr){
			del(this->ptr);
		}
		this->ptr = ptr;
	}

	T& operator*(void){
		return *ptr;
	}

	T* operator->(void){
		return ptr;
	}
	
	operator bool(void)const{
		return ptr != nullptr;
	}
	
	Deleter get_deleter(void)const{
		return del;
	}

private:
	T *ptr;
	Deleter del;
};

//针对数组 进行 局部特化  偏特化
template<class T,class Deleter>
class UniquePtr<T[],Deleter>{
public:
	UniquePtr(T *ptr = nullptr):ptr(ptr){
	
	}
	~UniquePtr(){
		if(ptr!=nullptr){
			del(ptr);
		}
	}
	T& operator[](size_t n){
		return ptr[n];
	}
private:
	T *ptr;
	Deleter del;
};


struct A{
	int x;
	int y;
	A(int x=0,int y=0):x(x),y(y){
		cout << "A()" << endl;
	}

	~A(){
		cout << "~A()" << endl;
	}

	void setx(int x){
		this->x = x;
	}
	void sety(int y){
		this->y = y;
	}

	void func(){
		cout << "(" << x << "," << y << ")" << endl;
	}
};


void func(){
	UniquePtr<A> up1(new A(1024,9527));
	throw int(1);
}


int main(int argc,char *argv[]){
	try{
		func();
	}catch(...){
	
	}
	
	UniquePtr<A> up(new A(1024,9527));
	cout << (*up).x << endl;
	cout << (*up).y << endl;
	up->func();
	
	UniquePtr<A> up1(move(up));	
	cout << boolalpha << bool(up) << endl;
	cout << bool(up1) << endl;
		
	UniquePtr<A> up2(new A(111,222));
	up2 = move(up1);
	cout << bool(up1) << endl;
	
	cout << up1.get() << endl;
	cout << up2.get() << endl;

	up2.reset(new A(1,2));

	A *pa = up2.release();
	delete pa;
	
	pa = new A[3];
	UniquePtr<A[]> up4(pa);


	cout << "main end" << endl;

    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值