基础知识
裸指针存在的问题
- 忘记释放资源导致资源泄漏(发生内存泄漏问题)
- 同一资源释放多次,导致释放野指针,程序崩溃
- 明明代码的后面写了资源释放的代码,但是由于程序的逻辑满足条件,从中间return掉了,导致释放资源的代码未被执行到
- 代码运行过程中发生异常,随着异常栈展开,导致释放资源的代码未被执行到
智能指针
保证做到资源的自动释放,用户可以不关注资源的释放,因为智能指针会帮助你完全管理资源的释放,它会保证无论程序逻辑怎么跑,正常执行或者产生异常,资源在到期的情况下,一定会进行释放。
C++11库里面,提供了带引用计数的智能指针和不带引用计数的智能指针,本文主要介绍原理和应用场景。
自己实现智能指针
利用栈上的对象出作用域自动析构的特征做到自动释放,把资源释放的代码全部放在析构函数中执行,就达到了所谓的智能指针。
#include<iostream>
using namespace std;
template<typename T>
class CSmartPtr
{
public:
CSmartPtr(T* ptr = nullptr)
:mptr(ptr){}
T& operator*()
{
return *mptr;
}
T* operator->()
{
return mptr;
}
~CSmartPtr()
{
delete mptr;
}
private:
T* mptr;
};
int main()
{
CSmartPtr<int> ptr1(new int);
*ptr1 = 20;
class Test
{
public:
void test()
{
cout << "call Test::test()" << endl;
}
};
CSmartPtr<Test> ptr2(new Test());
ptr2->test();
return 0;
}
上面的智能指针主要用到了两点:
- 智能指针体现了把裸指针进行一次面向对象的封装,在构造函数中初始化资源地址,在析构函数中释放资源
- 利用栈上的对象做出作用域自动析构这个特点,在智能指针的析构函数中保证释放资源
智能指针不能直接用于堆上,如CSmartPtr *p=new CSmartPtr(new int)是错误的。
不带引用计数的智能指针
int main()
{
CSmartPtr<int> p1(new int);
CSmartPtr<int> p2(p1);
return 0;
}
上面代码运行直接崩溃,因为默认的拷贝构造函数做的是浅拷贝,两个智能指针都持有一个new int资源,到ptr1析构的时候,ptr2先析构释放资源了,到ptr1析构的时候,就成了delete野指针了,造成程序崩溃。所以智能指针就要解决浅拷贝问题和保证多指针指向同一资源的时候只能释放一次
- auto_ptr(C++库里面)
不推荐使用auto_ptr。从C++源码可知只有最后一个auto_ptr智能指针持有资源,原来的auto_ptr都赋成nullptr了。
int main()
{
auto_ptr<int> ptr1(new int);
auto_ptr<int> ptr2(ptr1);
*ptr2 = 20;
cout << *ptr1 << endl;
return 0;
}
能否在容器中使用auto_ptr?
vector<auto_ptr> vec1;vec2(vec1);
不能,因为容器使用过程中,可能会做容器的拷贝构造和赋值,就会引起容器里面每一个元素的拷贝构造和赋值,如果说给容器里面全部放的auto_ptr,用vec1构造一个新的vec2容器出来,那么vec1底层的裸指针会被全部置成nullptr,所以auto_ptr不要做过于复杂的操作,会转移对象的所有权。
总结:auto_ptr智能指针不带引用计数,解决浅拷贝问题,是直接把前面的auto_ptr都置为nullptr,只让最后一个auto_ptr持有资源。
- scoped_ptr(C++11新标准,仅仅只能使用单个对象,使用的少)
scoped_ptr(const scoped_ptr<T>&)=delete;
scoped_ptr<T>&operator=(const scoped_ptr<T>&)=delete;
scoped_ptr智能指针私有化了拷贝构造函数和赋值函数,从根本上杜绝了智能指针浅拷贝,所以scoped_ptr也是不能用在容器当中的,如果容器互相进行拷贝或者赋值,就会引起scoped_ptr对象的拷贝构造和赋值,这是不允许的,代码会提示错误。
auto_ptr和scoped_ptr的区别:
auto_ptr会任意转移资源的所有权,scoped_ptr不会转移资源的所有权,因为拷贝构造和赋值都被禁止了
- unique_ptr(C++11新标准,推荐使用)
unique_ptr(const unique_ptr<T>&)=delete;
unique_ptr<T>&operator=(const unique_ptr<T>&)=delete;
unique_ptr(unique_ptr<T> &&src)
unique_ptr<T>&operator=(unique_ptr<T>&&src)
template<typename T>
unique_ptr<T> getSmartPtr()
{
unique_ptr<T> ptr(new T())
return ptr;
}
unique_ptr<T> ptr=getSmartPtr<int>()
ptr1=getSmartPtr<int>()
int main()
{
unique_ptr<int> p1(new int);
unique_ptr<int> p2(std::move(p1));
return 0;
}
unique_ptr有一点和scoped_ptr一样,就是去掉了拷贝构造函数和赋值函数,但是unique_ptr提供了带右值引用参数的拷贝构造和赋值。
实现带引用计数的智能指针
带引用计数的智能指针shared_ptr和weak_ptr
带引用计数:多个智能指针可以管理同一个资源
带引用计数:给每一个资源对象,匹配一个引用计数;
智能指针 =》资源的时候 =》引用计数+1
智能指针 =》不使用资源的时候 =》引用计数-1 =》!=0 0资源释放了
#include<iostream>
#include<memory>
using namespace std;
template<typename T>
class RefCnt
{
public:
RefCnt(T* ptr = nullptr)
:mptr(ptr)
{
if (mptr != nullptr)
mcount = 1;
}
void addRef(){ mcount++; }
int delRef(){ return --mcount; }
private:
T* mptr;
int mcount;
};
template<typename T>
class CSmartPtr
{
public:
CSmartPtr(T* ptr = nullptr)
:mptr(ptr)
{
mpRefCnt = new RefCnt<T>(mptr);
}
CSmartPtr(const CSmartPtr<T>& src)
:mptr(src.mptr), mpRefCnt(src.mpRefCnt)
{
if (mptr != nullptr)
mpRefCnt->addRef();
}
CSmartPtr<T>& operator=(const CSmartPtr<T>& src)
{
if (this == &src)
return *this;
if (mpRefCnt->delRef() == 0)
{
delete mptr;
}
mptr = src.mptr;
mpRefCnt = src.mpRefCnt;
mpRefCnt->addRef();
return *this;
}
~CSmartPtr()
{
if (mpRefCnt->delRef() == 0)
{
delete mptr;
mptr = nullptr;
}
}
T& operator*()
{
return *mptr;
}
T* operator->()
{
return mptr;
}
private:
T* mptr;
RefCnt<T>* mpRefCnt;
};
int main()
{
CSmartPtr<int> ptr1(new int);
CSmartPtr<int> ptr2(ptr1);
CSmartPtr<int> ptr3;
ptr3 = ptr2;
*ptr1 = 20;
cout << *ptr2 << " " << *ptr3 << endl;
return 0;
}
shared_ptr的交叉引用问题
shared_ptr:强智能指针 可以改变资源的引用计数
weak_ptr:弱智能指针 不会改变资源的引用计数
weak_ptr观察shared_ptr,shared_ptr观察资源(内存),没有提供operator* operator->
强智能指针循环引用(交叉引用)是什么问题?什么结果?怎么解决?
造成new出来的资源无法释放!资源泄露问题! 定义对象的时候用强智能指针,引用对象的时候用弱智能指针
#include<iostream>
using namespace std;
class B;
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
shared_ptr<B> _ptrb;
};
class B
{
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
shared_ptr<A> _ptra;
};
int main()
{
shared_ptr<A> pa(new A());
shared_ptr<B> pb(new B());
pa->_ptrb = pb;
pb->_ptra = pa;
cout << pa.use_count() << endl;
cout << pb.use_count() << endl;
return 0;
}
强智能指针的循环引用问题导致资源无法释放
定义对象的时候用强智能指针,引用对象的时候用弱智能指针
#include<iostream>
using namespace std;
class B;
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
weak_ptr<B> _ptrb;
};
class B
{
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
weak_ptr<A> _ptra;
};
int main()
{
shared_ptr<A> pa(new A());
shared_ptr<B> pb(new B());
pa->_ptrb = pb;
pb->_ptra = pa;
cout << pa.use_count() << endl;
cout << pb.use_count() << endl;
return 0;
}
weak_ptr没有提供operator*和 operator->,可以将weak_ptr提升为shared_ptr,然后使用operator*和operator->方法
#include<iostream>
using namespace std;
class B;
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
void testA() { cout << "非常好用的方法!" << endl; }
weak_ptr<B> _ptrb;
};
class B
{
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
void func()
{
shared_ptr<A> ps = _ptra.lock();
if (ps != nullptr)
{
ps->testA();
}
}
weak_ptr<A> _ptra;
};
int main()
{
shared_ptr<A> pa(new A());
shared_ptr<B> pb(new B());
pa->_ptrb = pb;
pb->_ptra = pa;
cout << pa.use_count() << endl;
cout << pb.use_count() << endl;
pb->fun();
return 0;
}
多线程访问共享对象的线程安全问题
#include<iostream>
#include <thread>
using namespace std;
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
void testA() { cout << "非常好用的方法!" << endl; }
};
void handler01(A* q)
{
q->testA();
}
int main()
{
A* p = new A();
thread t1(handler01, p);
std::this_thread::sleep_for(std::chrono::seconds(2));
delete p;
t1.join();
return 0;
}
void handler01(A* q)
{
std::this_thread::sleep_for(std::chrono::seconds(2));
q->testA();
}
int main()
{
A* p = new A();
thread t1(handler01, p);
delete p;
return 0;
}
A对象已经被析构了就不能再调用了!!!
void handler01(weak_ptr<A> pw)
{
std::this_thread::sleep_for(std::chrono::seconds(2));
shared_ptr<A> sp = pw.lock();
if (sp != nullptr)
{
sp->testA();
}
else
{
cout << "A对象已经析构,不能再访问!" << endl;
}
}
int main()
{
{
shared_ptr<A> p(new A());
thread t1(handler01, weak_ptr<A>(p));
t1.detach();
}
std::this_thread::sleep_for(std::chrono::seconds(20));
return 0;
}
自定义删除器
智能指针的删除器:deletor
智能指针:能够保证资源的绝对释放
#include<iostream>
using namespace std;
#include<functional>
template<typename T>
class MyDeletor
{
public:
void operator()(T* ptr)const
{
cout << "call MyDeletor.operator()" << endl;
delete[]ptr;
}
};
template<typename T>
class MyFileDeletor
{
public:
void operator()(T* ptr)const
{
cout << "call MyFileDeletor.operator()" << endl;
fclose(ptr);
}
};
int main()
{
unique_ptr<int, function<void(int*)>> ptr1(new int[100],
[](int* p)->void {
cout << "call MyDeletor.operator()" << endl;
delete[]p;
}
);
unique_ptr<FILE, function<void(FILE*)>> ptr2(fopen("data.txt", "w"),
[](FILE* p)->void {
cout << "call MyFileDeletor.operator()" << endl;
fclose(p);
}
);
return 0;
}
|