1. 智能指针原理
智能指针的本质是一个类模板,它可以创建任何类型的指针对象,当该指针对象的生命周期结束时,会自动调用析构函数释放其对应的资源。
/// 智能指针实现
template <class T>
class Ptr
{
public:
Ptr(T* _ptr): ptr(_ptr) {}
~Ptr()
{
if (ptr)
{
cout << "ptr delete" << endl;
delete ptr;
ptr = nullptr;
}
}
private:
T* ptr;
};
以上是智能指针的简单实现,下面我们来使用这个指针:
int main()
{
Ptr<int> ptr1(new int(1));
return 1;
}
执行结果:
ptr delete
在生命周期结束后,ptr1会调用析构函数释放资源。
但是如果再创建一个智能指针指向同一个对象时:
int main()
{
Ptr<int> ptr1(new int(1));
Ptr<int> ptr2(ptr1);
return 1;
}
执行结果:
ptr delete
ptr delete
a.out(27135,0x104987d40) malloc: *** error for object 0x127606810: pointer being freed was not allocated
a.out(27135,0x104987d40) malloc: *** set a breakpoint in malloc_error_break to debug
zsh: abort ./a.out
程序会报错,提示:释放的指针没有被分配内存。
根据打印结果可以看出,析构函数被调用了两次,也就是说两个指针指向的对象被释放了两次。
所以这个智能指针存在问题,不能允许多个指针指向同一个对象。
2. auto_ptr
auto_ptr是c++98库中提供的智能指针,该指针对于上面的问题,采取的解决方案是:将原指针拷贝或赋值给新指针时,将原指针设置为nullptr,此时就只有新指针指向目标对象。
#include <iostream>
using namespace std;
int main()
{
auto_ptr<int> autoPtr1(new int(1));
auto_ptr<int> autoPtr2(autoPtr1);
cout << *autoPtr2 << endl;
cout << *autoPtr1 << endl;
return 1;
}
执行结果:
1
zsh: segmentation fault ./a.out
程序在访问原指针指向的对象时报错,因为这个原因,很多公司禁止使用这个智能指针。
3. unique_ptr
unique_ptr是c++11库中提供的指针,auto_ptr在c++11中被废弃,所以如果用-std=c++11是无法编译通过含有auto_ptr的代码。unique_ptr直接将拷贝构造函数和赋值函数禁用,不允许对该指针进行赋值和拷贝。
#include <iostream>
using namespace std;
int main()
{
unique_ptr<int> uniquePtr1(new int(1));
unique_ptr<int> uniquePtr2(uniquePtr1);
return 1;
}
编译报错:
main.cpp:26:18: error: call to implicitly-deleted copy constructor of 'unique_ptr<int>'
unique_ptr<int> uniquePtr2(uniquePtr1);
4. shared_ptr
shared_ptr也是c++11库中提供的智能指针,它允许多个指针指向同一个对象。并且会保证对象资源只会被释放一次。它的实现原理是在shared_ptr内部维护一个被引用计数器,被当目标对象被引用一次时,计数器加一,当引用减少一次时,计数器减一,当计数器为0时,调用析构函数,释放资源。
简单实现代码:
//todo
当然这个指针也存在一定的问题,当两个指针互相引用的时候,会出现计数器无法减到0,因此无法释放资源的问题。
#include<iostream>
using namespace std;
class B;
class A
{
public:
~A()
{
cout << "A delete " << endl;
}
void setPtr(shared_ptr<B> _ptr)
{
ptr = _ptr;
}
private:
shared_ptr<B> ptr;
};
class B
{
public:
~B()
{
cout << "B delete " << endl;
}
void setPtr(shared_ptr<A> _ptr)
{
ptr = _ptr;
}
private:
shared_ptr<A> ptr;
};
int main()
{
shared_ptr<A> spA(new A());
shared_ptr<B> spB(new B());
spA->setPtr(spB);
spB->setPtr(spA);
cout << "A.count: " << spA.use_count() << endl;
cout << "B.count: " << spB.use_count() << endl;
return 1;
}
执行结果:
A.count:2
B.count:2
从执行结果可以看出,程序并没用调用析构函数释放资源。想要解决这个问题,需要引入weak_ptr。
weak_ptr是为了配合shared_ptr而引人的一种智能指针,它在构造和析构的时候不会引起shared_ptr引用计数器增加或减少。
#include<iostream>
using namespace std;
class B;
class A
{
public:
~A()
{
cout << "A delete " << endl;
}
void setPtr(shared_ptr<B> _ptr)
{
ptr = _ptr;
}
private:
weak_ptr<B> ptr;
};
class B
{
public:
~B()
{
cout << "B delete " << endl;
}
void setPtr(shared_ptr<A> _ptr)
{
ptr = _ptr;
}
private:
weak_ptr<A> ptr;
};
int main()
{
shared_ptr<A> spA(new A());
shared_ptr<B> spB(new B());
spA->setPtr(spB);
spB->setPtr(spA);
cout << "A.count: " << spA.use_count() << endl;
cout << "B.count: " << spB.use_count() << endl;
return 1;
}
执行结果:
A.count:1
B.count:1
B delete
A delete
从执行结果看,引用计数器没有增加,生命周期结束后,资源被释放。
还有个问题,当智能指针指向一个数组的时候,因为share_ptr在释放资源时,默认调用的是:delete ptr,而不是delete[] ptr,所以此时程序会报错。
#include<iostream>
using namespace std;
class B;
class A
{
public:
~A()
{
cout << "A delete " << endl;
}
void setPtr(shared_ptr<B> _ptr)
{
ptr = _ptr;
}
private:
weak_ptr<B> ptr;
};
class B
{
public:
~B()
{
cout << "B delete " << endl;
}
void setPtr(shared_ptr<A> _ptr)
{
ptr = _ptr;
}
private:
weak_ptr<A> ptr;
};
int main()
{
shared_ptr<A> spA(new A[10]);
return 1;
}
执行结果:
A delete
a.out(27771,0x100f27d40) malloc: *** error for object 0x14c606820: pointer being freed was not allocated
a.out(27771,0x100f27d40) malloc: *** set a breakpoint in malloc_error_break to debug
zsh: abort ./a.out
从执行结果来看,释放指向数组的指针时,程序报错。这里需要增加一个删除器,share_ptr自身提供了一个可以定制的删除器,用来自定义删除方法。
#include<iostream>
using namespace std;
class B;
class A
{
public:
~A()
{
cout << "A delete " << endl;
}
void setPtr(shared_ptr<B> _ptr)
{
ptr = _ptr;
}
private:
weak_ptr<B> ptr;
};
class B
{
public:
~B()
{
cout << "B delete " << endl;
}
void setPtr(shared_ptr<A> _ptr)
{
ptr = _ptr;
}
private:
weak_ptr<A> ptr;
};
template<class T>
class DeleteArray
{
public:
void operator() (const T* array)
{
cout << "delete array[]" << endl;
delete[] array;
}
};
int main()
{
shared_ptr<A> spA(new A[3], DeleteArray<A>());
return 1;
}
执行结果:
delete array[]
A delete
A delete
A delete
从执行结果看,程序成功释放指针指向的数组中的所有对象资源。