auto_ptr(不建议使用)
所有权唯一,即只允许一个智能指针指向同一块堆内存。如果有两个智能指针,则回收旧智能指针的所有权。
缺点:指针的赋值或拷贝过程中,回收旧智能指针的所有权,导致旧智能指针无效,若之后给旧智能指针赋值,程序会报错。因此,不建议使用。
#include<iostream>
template<typename T>
class Auto_Ptr
{
public:
Auto_Ptr(T* ptr)
:mptr(ptr)
{}
Auto_Ptr(Auto_Ptr<T>& rhs)
{
mptr=rhs.Release();
}
Auto_Ptr<T>& operator=(Auto_Ptr<T>& rhs)
{
if (this != &rhs)
{
delete mptr;
mptr = rhs.Release();
}
return *this;
}
T* operator->()
{
return mptr;
}
T& operator*()
{
return *mptr;
}
~Auto_Ptr()
{
delete mptr;
}
private:
T* Release()
{
T* tmp = mptr;
mptr = NULL;
return tmp;
}
T* mptr;
};
int main()
{
Auto_Ptr<int> ap1(new int);
Auto_Ptr<int> ap2 = ap1;
*ap2 = 30;//正确
//*ap1 = 20;//错误,ap1已经失效
return 0;
}
改进方法:
带标志位的智能指针(库里没有):所有权不唯一,释放权唯一。
缺点:释放权转移,导致智能指针提前失效。
#include<iostream>
template<typename T>
class SmartPtr
{
public:
SmartPtr(T* ptr)
:mptr(ptr), flag(true)
{}
SmartPtr(SmartPtr<T>& rhs)
{
mptr = rhs.mptr;
flag = rhs.flag;
rhs.flag = false;
}
SmartPtr<T>& operator=(SmartPtr<T>& rhs)
{
if (this != &rhs)
{
this->~SmartPtr();
mptr = rhs.mptr;
flag = rhs.flag;
rhs.flag = false;
}
return *this;
}
~SmartPtr()
{
if (flag)//有释放权
{
delete mptr;
}
mptr = NULL;
}
T* operator->()
{
return mptr;
}
T& operator*()
{
return *mptr;
}
private:
T* mptr;
bool flag;//释放权的标识 true false
};
int main()
{
SmartPtr<int> sp1 = new int;
SmartPtr<int> sp2 = sp1;
*sp2 = 30;//正确
*sp1 = 20;//正确
return 0;
}
假设有个函数如下:
void getObject( SmartPtr<int>& arg)
{
SmartPtr<int> tmp = arg;
//...
}
//释放权转移,导致智能指针提前失效
int main()
{
SmartPtr<int> sp1 = new int(10);
getObject(sp1);
*sp1 = 30;//程序不会报错,但存在问题
return 0;
}
这个函数中存在对象tmp,这个对象在函数调用完成后退栈销毁,会把堆内存释放掉的。sp1对应的堆内存已经被释放掉,当主函数再次给sp1赋值时,会有问题。但该堆内存给当前应用程序分配过,所以检测不出来。
unique_ptr
所有权唯一,不允许权限转移。实现时将拷贝构造函数和赋值运算符重载函数写在私有下。
缺点:通过其他手段使多个智能指针指向同一堆内存,可能会释放失败(具体如下)。
#include<iostream>
template<typename T>
class Unique_Ptr
{
public:
Unique_Ptr(T* ptr)
:mptr(ptr)
{}
~Unique_Ptr()
{
delete mptr;
}
T* operator->()
{
return mptr;
}
T& operator*()
{
return *mptr;
}
private:
Unique_Ptr(const Unique_Ptr<T>&);
Unique_Ptr<T>& operator=(const Unique_Ptr<T>&);
T* mptr;
};
int main()
{
Unique_Ptr<int> up1(new int);
Unique_Ptr<int> up2(up1);//设计点不允许多个智能指针指向同一堆内存,错误
return 0;
}
我们用其他手段可以让多个智能指针指向同一堆内存(如下),程序能够编译通过,但在程序结束时,up3的销毁已经释放了堆内存,up2、up1销毁时,会导致同一块堆内存被重复释放。
int main()
{
int*p = new int;
Unique_Ptr<int> up1(p);
Unique_Ptr<int> up2(p);
Unique_Ptr<int> up3(p);
return 0;
}
shared_ptr(常用)
允许多个智能指针指向同一块堆内存。最后一个销毁的对象释放该堆内存。也叫做带引用计数的智能指针、强智能指针。
缺点:相互引用的时候可能会出现问题。
#include<iostream>
class Ref_Management
{
public:
Ref_Management()
{
current = 0;
}
void addRef(void* ptr)
{
int index = FindIndex(ptr);
if (index < 0)
{
node[current].addr = ptr;
node[current].count = 1;
current++;
}
else
{
node[index].count++;
}
}
void delRef(void* ptr)
{
int index = FindIndex(ptr);
if (index < 0)
{
throw std::exception("ref is error!");
}
else
{
if (getRef(ptr) > 0)
{
node[index].count--;
}
}
}
int getRef(void* ptr)
{
int index = FindIndex(ptr);
if (index < 0)
{
return -1;
}
else
{
return node[index].count;
}
}
private:
int FindIndex(void* ptr)//找地址对应的下标
{
for (int i = 0; i < 10; i++)
{
if (node[i].addr == ptr)
{
return i;
}
}
return -1;
}
class Node
{
public:
Node(void* add = NULL, int cnt = 0)
:addr(add), count(cnt)
{}
public:
void* addr;
int count;
};
Node node[10];//这里未考虑扩容
int current;//有效元素个数
};
template<typename T>
class Shared_Ptr
{
public:
Shared_Ptr(T* ptr = NULL)
:mptr(ptr)
{
rm.addRef(mptr);
}
Shared_Ptr(const Shared_Ptr<T>& rhs)
{
mptr = rhs.mptr;
rm.addRef(mptr);
}
Shared_Ptr<T>& operator=(const Shared_Ptr<T>& rhs)
{
if (this != &rhs)
{
rm.delRef(mptr);
if (rm.getRef(mptr) == 0)
{
delete mptr;
}
mptr = rhs.mptr;
rm.addRef(mptr);
}
return *this;
}
~Shared_Ptr()
{
rm.delRef(mptr);
if (rm.getRef(mptr) == 0)
{
delete mptr;
}
mptr = NULL;
}
T* operator->()
{
return mptr;
}
T& operator*()
{
return *mptr;
}
T* getPtr()const
{
return mptr;
}
private:
T* mptr;
static Ref_Management rm;
};
template<typename T>
Ref_Management Shared_Ptr<T>::rm;
int main()
{
int* p = new int;
Shared_Ptr<int> sp1(p);
Shared_Ptr<int> sp2(p);//正确
Shared_Ptr<int> sp3(p);//正确,同一堆内存被多个智能指针指向
Shared_Ptr<int> sp4(new int);
sp2 = sp4;//正确
return 0;
}
相互引用问题:
//存在以下两个类
class B;
class A
{
public:
A()
{
std::cout << "A::A()" << std::endl;
}
~A()
{
std::cout << "A::~A()" << std::endl;
}
public:
Shared_Ptr<B> spa;
};
class B
{
public:
B()
{
std::cout << "B::B()" << std::endl;
}
~B()
{
std::cout << "B::~B()" << std::endl;
}
public:
Shared_Ptr<A> spb;
};
int main()
{
Shared_Ptr<A> pa = new A();
Shared_Ptr<B> pb = new B();
pa->spa = pb;
pb->spb = pa;
return 0;
}
相互引用之后如下:
程序的运行结果如下:
程序结束后,根本没有调动A和B的析构函数,导致内存泄露。
weak_ptr
主要是为了解决强智能指针(shared_ptr)相互引用的问题。不能单独使用,要与强智能指针一起使用。
接着上述(强智能指针)程序,设计弱智能指针如下:
template<typename T>
class Weak_Ptr
{
public:
Weak_Ptr(T* ptr = NULL)
:mptr(ptr)
{}
Weak_Ptr(const Weak_Ptr<T>& rhs)
{
mptr = rhs.mptr;
}
Weak_Ptr<T>& operator=(const Weak_Ptr<T>& rhs)
{
if (this != &rhs)
{
mptr = rhs.mptr;
}
return *this;
}
Weak_Ptr<T>& operator=(const Shared_Ptr<T>& rhs)
{
mptr = rhs.getPtr();
return *this;
}
~Weak_Ptr()
{}
T* operator->()
{
return mptr;
}
T& operator*()
{
return *mptr;
}
private:
T* mptr;
};
int main()
{
Shared_Ptr<A> pa = new A();
Shared_Ptr<B> pb = new B();
pa->spa = pb;
pb->spb = pa;
return 0;
}
这时程序的运行结果如下,正确: