C++ 智能指针的实现
Sep 5, 2017
1. shared_ptr 的特性 #
shared_ptr内部维护一个引用计数,当创建、copy、销毁时,引用计数都会变化
(1) 拷贝构造函数 p(q),会递增q的引用,p和q指向同一个对象 #
shared_ptr<string> q = make_shared<string>("smartptr");
shared_ptr<string> p(q); //递增q的引用计数,p和q指向同一个对象
std::cout << "p use_count=" << p.use_count() << std::endl; // 2
std::cout << "q use_count=" << q.use_count() << std::endl; // 2
(2) 赋值构造函数 p=q,会递增q的引用,递减p的引用 #
class Man
{
public:
Man(string name) :_name(name) { std::cout << _name << ": Create" << std::endl; };
~Man() { std::cout << _name << ": Delete" << std::endl; };
private:
string _name;
};
shared_ptr<Man> q = make_shared<Man>("Man_q");
shared_ptr<Man> p = make_shared<Man>("Man_p");
std::cout << "p use_count=" << p.use_count() << std::endl; // 1
std::cout << "q use_count=" << q.use_count() << std::endl; // 1
p = q; //递增q的引用计数,递减p的引用,当p引用为0时,释放其内部的对象。p和q指向同一个对象
std::cout << "p use_count=" << p.use_count() << std::endl; // 2
std::cout << "q use_count=" << q.use_count() << std::endl; // 2
上面这个示例代码,当 执行 p = q时,
- (1) 递减p的引用计数,p之前的引用计数为1,递减后为0
- (2) 如果p的引用计数为0,则释放p中原有的对象,此时会调用Man的析构,打印"Man_p :Delete"
- (3) 递增q的引用计数,即变为2
- (4) 将 p 指向 q
2. 实现一个智能指针SmartPtr #
理解了上面的过程之后,就可以动手实现一个智能指针
template <class _Ty>
class SmartPtr
{
public:
SmartPtr(_Ty* ptr) :_refptr(new RefPtr(ptr)) {
};
SmartPtr(const SmartPtr& obj){ // p = q
if (this != &obj){
if (_refptr){
*(_refptr->_count) -= 1; // 递减p的引用计数
if (*(_refptr->_count) == 0){ // 如果p的引用计数为0,则删除
delete _refptr;
}
}
_refptr = obj._refptr; // 将p指向q
*(_refptr->_count) += 1; // 递增q的引用计数
}
}
SmartPtr& operator=(const SmartPtr& obj){ // 同上
if (this != &obj){
if (_refptr){
*(_refptr->_count) -= 1;
if (*(_refptr->_count) == 0){
delete _refptr;
}
}
_refptr = obj._refptr;
*(_refptr->_count) += 1;
}
return *this;
}
~SmartPtr(){
*(_refptr->_count) -= 1; // 析构时,递减引用计数
if (*(_refptr->_count) == 0){ // 如果引用计数为0,则删除
delete _refptr;
}
}
int use_count() { return *(_refptr->_count); }
private:
// 辅助类,用于保存引用计数 和 实际对象
// 之所以需要使用辅助类,而不是直接用int _count和 _Ty* 成员变量
// 是为了能否封装住 _count和_Ty* 这些变量,对外不可见
class RefPtr {
friend class SmartPtr;
RefPtr(_Ty* ptr) :_ptr(ptr) { _count = new int(1); };
~RefPtr() { if (_ptr) delete _ptr; delete _count; };
int* _count;
_Ty* _ptr;
};
RefPtr* _refptr;
};
这里有几个细节
- (1) 将SmartPtr声明为RefPtr的友元之后,SmartPtr就可以访问RefPtr的私有变量
- (2) RefPtr全部为private,可以很好的封装不对外可见
- (3) _count 声明为int* 指针,这样,在拷贝构造和赋值构造中,就可以对 const SmartPtr& obj 这个obj对象的count也进行改变,因为this->_refptr->_count和 obj的count指向同个内存区;再者对this->_refptr->_count 做递减时,也会影响到其他指向this的智能指针的引用计数
使用示例如下
SmartPtr<Man> man1(new Man("Man1"));
{
std::cout << "Cnt1:" << man1.use_count() << std::endl; // 1
SmartPtr<Man> man2(man1); //man2 是man1的copy
std::cout << "Cnt1:" << man1.use_count() << std::endl; // 2
std::cout << "Cnt2:" << man2.use_count() << std::endl; // 2
{
SmartPtr<Man> man3(new Man("Man3"));
std::cout << "Cnt1:" << man1.use_count() << std::endl; // 2
std::cout << "Cnt2:" << man2.use_count() << std::endl; // 2
std::cout << "Cnt3:" << man3.use_count() << std::endl; // 1
man3 = man1; //man3先析构了Man3, 再指向了man1, 同时引用+1 ; Man3: Delete
std::cout << "Cnt1:" << man1.use_count() << std::endl; // 3
std::cout << "Cnt2:" << man2.use_count() << std::endl; // 3
std::cout << "Cnt3:" << man3.use_count() << std::endl; // 3
}
std::cout << "Cnt1:" << man1.use_count() << std::endl; // 2 因为man3 出了作用域,析构了
std::cout << "Cnt2:" << man2.use_count() << std::endl; // 2
}
std::cout << "Cnt1:" << man1.use_count() << std::endl; // 1 因为man2 出了作用域,析构了
输出如下
Man1: Create
Cnt1:1
Cnt1:2
Cnt2:2
Man3: Create
Cnt1:2
Cnt2:2
Cnt3:1
Man3: Delete
Cnt1:3
Cnt2:3
Cnt3:3
Cnt1:2
Cnt2:2
Cnt1:1