在C++11的标准中,引入了智能指针的概念。
相比于auto_ptr而言,其主要缺陷在于在进行指针拷贝的时候,会出现管理权转移的问题,导致原指针最终编成一个悬挂指针(dangling pointer)。
而智能指针shared_ptr, weak_ptr, 以及unique_ptr则各有侧重的解决相关的问题。具体可参考:
引入智能指针,不代表就不会出现问题。在使用不当的情况下,也会发生访问空指针等情况。
本文将简单的介绍,C++11中的智能指针的引用计数大致是如何实现的。部分内容节选自 C++ primer 5th。
首先,引用计数的工作方式如下:
- 除了初始化对象外,每个构造函数(拷贝构造函数除外)还要创建一个引用计数,用来记录与多少对象与正在创建的对象共享类型。当我们创建一个对象时,只有一个对象共享状态,因此将引用计数初始化为1
- 拷贝构造函数不分配新的计数器,而是拷贝构造函数给定的对象的数据成员,包括计数器。拷贝构造函数递增共享的计数器,指出给定的对象的状态又被一个新用户所共享
- 析构函数递减计数器,指出共享状态的用户少了一个。如果计数器变为0,则析构函数释放状态。
- 拷贝赋值运算符递增右侧运算对象的计数器,递减左侧运算对象的计数器。如果左侧运算对象的计数器变为0,意味着他的共享状态没有用户,拷贝赋值运算符就必须销毁对象。
唯一的问题:确定在哪里存放引用计数。答案是:将计数器保存在动态内存中
以下程序,将引用计数的工作方式进行了完美的诠释:
#include <string>
class HasPtr {
public:
// constructor allocates a new string and a new counter, which it sets to 1
HasPtr(const std::string &s = new std::string()) :
ps(new std::string(s)), i(0), use(new std::size_t (1)) {}
// copy constructor copies all three data members and increments the counter
HasPtr(const HasPtr &p) :
ps(p.ps), i(p.i), use(p.use) { ++ *use; }
HasPtr& operator= (const HasPtr& rhs) {
// increment the use count of the right-hand operand
++*rhs.use;
if(--*use == 0) { // then decrement this object’s counter
delete ps; // if no other users
delete use; // free this object’s allocated members
}
ps = rhs.ps; // copy data from rhs into this object
i = rhs.i;
use = rhs.use;
return *this; // return this object
}
~HasPtr() {
if(--*use == 0) { // if the reference count goes to 0
delete ps; // delete the string
delete use; // and the counter
}
}
private:
std::string *ps;
int i;
// member to keep track of how many objects share *ps
size_t *use; // --> reference counter
};