C++在用引用取缔掉指针的同时,模板的引入带给了指针新的发挥空间
智能指针简单的来说就是带有不同特性和内存管理的指针模板
首先unique_ptr实现起来其实很简单,只需要对赋值函数(=运算符重载)和拷贝构造函数等这些利用对象创建新的对象的函数做出修改即可,当然析构函数部分的释放也是要注意的。
template<typename T>
class unique_ptr{
T*ptr;
public:
unique_ptr(T*p=nullptr):ptr(p){}
unique_ptr(unique_ptr&&other) noexcept:ptr(other.release()){
other.ptr=nullptr;
}
unique_ptr& operator=(unique_ptr&&other) noexcept{
if(other.ptr!=this->ptr){
change(other.release());
}
return *this;
}
T*get()const{
return ptr;
}
T& operator*() const{
return *ptr;
}
T* operator->() const{
return ptr;
}
explicit operator bool() const{
return ptr!=nullptr;
}
~unique_ptr(){
delete ptr;
ptr=nullptr;
}
T*release(){
T*tmp=this->ptr;
this->ptr=nullptr;
return tmp;
}
void change(T*cur=nullptr){
if(cur!=ptr){
delete ptr;
ptr=cur;
}
}
unique_ptr(const unique_ptr&)=delete;
unique_ptr& operator=(const unique_ptr&)=delete;
};
然后看shared_ptr它的话要比unique_ptr要自由的多实现起来也很简单,只需要多一步计数操作即可。
template<typename T>
class shared_ptr{
T*ptr;
int* cnt;
public:
shared_ptr(T*p=nullptr):ptr(p),cnt(new int(1)){}
shared_ptr(const shared_ptr&other) noexcept:ptr(other.ptr),cnt(other.cnt){
(*cnt)++;
}
shared_ptr& operator=(const shared_ptr&other) noexcept{
if(this!=&other){
release();
this->ptr=other.ptr;
this->cnt=other.cnt;
(*cnt)++;
}
return *this;
}
T*get()const{
return ptr;
}
T& operator*() const{
return *ptr;
}
T* operator->() const{
return ptr;
}
explicit operator bool() const{
return ptr!=nullptr;
}
~unique_ptr(){
release();
}
void release(){
if(cnt&&--(*cnt)==0){
delete cnt;
delete ptr;
}
ptr=nullptr;
cnt=nullptr;
}
};
但是注意shared_ptr的计数可能会带来一个问题:循环引用
看下面代码其实很好理解,就是循环指向造成计数到不了0从而释放不了对象。
class B;
class A{
public:
shared_ptr<B>aptr;
};
class B{
public:
shared_ptr<A>bptr;
};
int main(){
shared_ptr<A>a=new A;
shared_ptr<B>b=new B;
a->aptr=b;
b->bptr=a;
}
解决方式:引入了weak_ptr搭配shared_ptr使用,weak_ptr是对对象的一种弱引用,它不会增加对象的use_count,weak_ptr和shared_ptr可以相互转化,shared_ptr可以直接赋值给weak_ptr,weak_ptr也可以通过调用lock函数来获得shared_ptr。
template <typename T>
class MySharedPtr;
template <typename T>
class MyWeakPtr;
template <typename T>
class MySharedPtr {
private:
T* ptr;
int* refCount;
int* weakCount;
public:
explicit MySharedPtr(T* p = nullptr) : ptr(p), refCount(new int(1)), weakCount(new int(0)) {}
MySharedPtr(const MySharedPtr& other) : ptr(other.ptr), refCount(other.refCount), weakCount(other.weakCount) {
(*refCount)++;
}
~MySharedPtr() {
release();
}
int use_count() const {
return (refCount ? *refCount : 0);
}
T* get() const {
return ptr;
}
T& operator*() const {
return *ptr;
}
T* operator->() const {
return ptr;
}
MyWeakPtr<T> weak_ptr() {
(*weakCount)++;
return MyWeakPtr<T>(this->ptr, this->refCount, this->weakCount);
}
MySharedPtr& operator=(const MySharedPtr& other) {
if (this != &other) {
release();
ptr = other.ptr;
refCount = other.refCount;
weakCount = other.weakCount;
(*refCount)++;
}
return *this;
}
private:
void release() {
if (refCount) {
(*refCount)--;
if (*refCount == 0) {
delete ptr;
delete refCount;
if (*weakCount == 0) {
delete weakCount;
}
}
}
}
friend class MyWeakPtr<T>;
};
template <typename T>
class MyWeakPtr {
private:
T* ptr;
int* refCount;
int* weakCount;
public:
MyWeakPtr() : ptr(nullptr), refCount(nullptr), weakCount(nullptr) {}
MyWeakPtr(T* p, int* rc, int* wc) : ptr(p), refCount(rc), weakCount(wc) {}
MyWeakPtr(const MyWeakPtr& other) : ptr(other.ptr), refCount(other.refCount), weakCount(other.weakCount) {
(*weakCount)++;
}
~MyWeakPtr() {
release();
}
MySharedPtr<T> lock() {
if (expired()) {
return MySharedPtr<T>();
}
(*refCount)++;
return MySharedPtr<T>(ptr, refCount, weakCount);
}
MyWeakPtr& operator=(const MyWeakPtr& other) {
if (this != &other) {
release();
ptr = other.ptr;
refCount = other.refCount;
weakCount = other.weakCount;
(*weakCount)++;
}
return *this;
}
bool expired() const {
return (refCount == nullptr || *refCount == 0);
}
private:
void release() {
if (weakCount) {
(*weakCount)--;
if (*weakCount == 0) {
delete weakCount;
}
}
}
};
shared_ptr和weak_ptr可以互相构造,看下面的代码就知道它们是如何解决循环引用的情况
class B;
class A{
public:
MYShared_Ptr<B>aptr;
};
class B{
public:
MYWeak_Ptr<A>bptr;
};
int main(){
MYShared_Ptr<A>a=new A;
MYShared_Ptr<B>b=new B;
a->aptr=b.lock();
b->bptr=a;
}
简单的来说就是通过两个可以互相转换的类型避免产生同种类型的环,对不同类型的环它们释放的时候互不影响对方的计数所以可以正常释放。