文章目录
- 1. 什么是引用计数
- 2. 引用计数的实现
- 3. 示例代码
1. 什么是引用计数
引用计数(reference count)的核心思想是使用一个计数器来标识当前指针指向的对象被多少类的对象所使用(即记录指针指向对象被引用的次数)。它允许有多个相同值的对象共享这个值的实现。引用计数的使用常有两个目的:
-
简化跟踪堆中(也即C++中new出来的)的对象的过程。一旦一个对象通过调用new被分配出来,记录谁拥有这个对象是很重要的,因为其所有者要负责对它进行delete。但是对象所有者可以有多个,且所有权能够被传递,这就使得内存跟踪变得困难。引用计数可以跟踪对象所有权,并能够自动销毁对象。可以说引用计数是个简单的垃圾回收体系。
-
节省内存,提高程序运行效率。如果很多对象有相同的值,为这多个相同的值存储多个副本是很浪费空间的,所以最好做法是让左右对象都共享同一个值的实现。C++标准库中string类采取一种称为”写时复制“的技术,使得只有当字符串被修改的时候才创建各自的拷贝,否则可能(标准库允许使用但没强制要求)采用引用计数技术来管理共享对象的多个对象。
2. 引用计数的实现
使用引用计数实现智能指针的关键是,引用计数应该存在哪里。引用计数应该是某个类对象和其复制对象共享的, 而指针成员恰好有这样的特性, 故可以在类中多声明一个size_t* 的成员,用来表示引用计数。
- 构造函数中创建类的新对象时,初始化引用计数为1;
- 拷贝构造函数复制指针,并使相应的引用计数增加1;
- 赋值操作减少左操作数所指对象的引用计数,增加右操作数所指对象的引用计数;
- 析构函数使引用计数减少1,并且当引用计数为1时,释放指针所指向的对象;
3. 示例代码
#include <iostream>
#include <string>using std::string; using std::ostream; using std::cout; using std::size_t;class HasPtr {friend ostream& print(ostream&, const HasPtr&);
public:// string():构造空的string类对象,既空字符串HasPtr(const string& s = string()) : ps(new string(s)), i(0), use(new size_t(1)) { } // constructor~HasPtr(); // 析构函数HasPtr(const HasPtr& rhs) : ps(rhs.ps), i(rhs.i), use(rhs.use) { ++* use; } // 拷贝构造函数HasPtr& operator=(const HasPtr&); // 运算符重载
private:string* ps; // ps是一个指针,指向string类型int i;size_t* use; // use是一个指针,指向size_t类型
};// 析构函数的定义
HasPtr::~HasPtr()
{if (-- * use == 0) { // 如果引用计数为变0delete ps; // 释放string内存delete use; // 释放计数器内存}
}// 运算符重载的定义
HasPtr&
HasPtr::operator=(const HasPtr& rhs)
{++* rhs.use; // 递增右侧运算对象的引用计数if (-- * use == 0) { // 然后递减本对象的引用计数delete ps; // 如果没有其他用户delete use; // 释放本对象分配的成员}ps = rhs.ps; // 将数据从rhs拷贝到本对象i = rhs.i;use = rhs.use;return *this; // 返回本对象
}// 友元函数的定义
ostream& print(ostream& os, const HasPtr& p)
{os << p.ps << ' ' << *p.ps << ' ' << *p.use;return os;
}void func(const HasPtr& p)
{HasPtr temp;temp = p;cout << "p: ";print(cout, p) << '\n';;cout << "temp: ";print(cout, temp) << '\n';;
}int main()
{cout << "HasPtr str1(\"copy me\"), str2;\n";HasPtr str1("copy me"), str2;cout << "str1: ";print(cout, str1) << '\n';cout << "str2: ";print(cout, str2) << '\n';cout << "\nstr2 = str1;\n";str2 = str1;cout << "str1: ";print(cout, str1) << '\n';cout << "str2: ";print(cout, str2) << '\n';cout << "\nfunc(str1);\n";func(str1);cout << "\nstr1: ";print(cout, str1) << '\n';cout << "str2: ";print(cout, str2) << '\n';return 0;}
输出结果:
HasPtr str1("copy me"), str2;
str1: 0116D990 copy me 1
str2: 011654E8 1str2 = str1;
str1: 0116D990 copy me 2
str2: 0116D990 copy me 2func(str1);
p: 0116D990 copy me 3
temp: 0116D990 copy me 3str1: 0116D990 copy me 2
str2: 0116D990 copy me 2