智能指针
包含于头文件 #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释放申请的动态内存。
setjmp
和longjmp
在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_ptr | shared_ptr | weak_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;
}