1.引用计数管理内存的原理
C++新标准库中采两个智能指针类型来管理动态对象,share_ptr允许多个指针指向同一个对象;unique_ptr则“独占”所指对象。
对于share_ptr<T>我们可以按如下方式使用去管理堆内存,相比于new,省去使用delete去释放内存的麻烦。
void use_factory(T arg)
{
share_ptr<Foo> p = factory(arg);
Foo *ptr = new Foo(arg);
//使用P
}//p离开了作用域,但它指向的内存会被释放掉
//ptr申请的内存没有被释放,造成了内存泄漏
我们知道指针或引用在离开作用域时是不会进行析构的,但类在离开作用域时会自动执行析构函数(从这也可看出share_ptr本质是类,只是模仿出指针使用的效果而已),因此,
我们可以通过析构函数调用delete去销毁资源。
那share_ptr如何实现多个类同时指向同一个对象,这就产生了引用计数,它实际是一个整形指针,让多个
对象共享其指向的对象,该对象记录着当前有多少个对象在共享同一变量。
share_ptr<Foo> get_factory(T arg)
{
share_ptr<Foo> p = factory(arg); //引用计数值为1,同时new一个Foo对象
share_ptr<Foo> tmp = factory(arg);//创建一个临时的tmp,
tmp = p; //进行赋值操作时,右侧P递增引入计数值为2,左侧tmp递减引入计数值为0,
//销毁上句创建的资源后再将指针指向与p同样的资源
return p; //当返回p时,引用计数进行了递增操作值为3
}//tmp离开作用域时,调用析构函数引用计数递减到2
//p离开作用域时,调用析构函数引用计数递减到1,表示还有一个对象享有Foo对象
根据上述例子我们知道shared_ptr通过拷贝构造和拷贝赋值函数递增或递减引用计数。
2.引用计数的实现(可参考c++ primer 13.2.2章节)
示例中定义了一个HasPtr类去管理一个string对象,其类的定义如下,为了便宜调试,每个类
均有打印信息。
①类定义
#include <string>
#include <iostream>
using std::string;
using std::cout;
using std::endl;
using std::ostream;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
class HasPtr{
friend ostream& operator<<(ostream&, const HasPtr&);
friend void swap(HasPtr& lhs, HasPtr& rhs);
public:
//构造函数
HasPtr(const string &s = string()):ps(new string(s)),use(new std::size_t(1))
{cout<< "call: HasPtr()"<<endl;}
HasPtr(const char *c):HasPtr(string(c)){}
//构造函数
HasPtr(const HasPtr& p):ps(p.ps), use(p.use)
{++*use; cout<< "call: HasPtr(const HasPtr& p)"<<endl;}
//拷贝构造函数
HasPtr& operator=(HasPtr p);
//虚构函数
~HasPtr();
private:
string *ps;
std::size_t *use;
};
②.拷贝赋值的实现拷贝赋值时为了避免自赋值的情况,本例采用swap的方式实现,基本过程是左侧对象执行一次析构后执行拷贝构造操作。
inline
void swap(HasPtr& lhs, HasPtr& rhs){
using std::swap;
swap(lhs.ps, rhs.ps);
swap(lhs.use, rhs.use);
}
inline
HasPtr& HasPtr::operator=(HasPtr p){
cout<< "void swap(HasPtr& lhs, HasPtr& rhs)"<<endl;
using std::swap;
swap(*this, p);
return *this;
}
③.析构的实现HasPtr::~HasPtr()
{
cout<< "HasPtr::~HasPtr():"<< *this <<endl;
if(--*use == 0)
{
cout<< "delete ps; delete use;"<<endl;
delete ps;
delete use;
}
}
④.打印函数inline
ostream& operator<<(ostream& os, const HasPtr& ptr)
{
os << *ptr.ps << " " << *ptr.use;
return os;
}
⑤.调试程序int main(int argc, char** argv)
{
cout << "1.构造函数" << endl;
HasPtr ptr1("first"); //打印:call: HasPtr()
HasPtr ptr2("sencond"); //打印:call: HasPtr()
cout << "" << endl;
cout << "2.拷贝构造" << endl;
HasPtr ptr3(ptr2); //打印:call: HasPtr(const HasPtr& p)
cout << "ptr1: " << ptr1 << endl; //打印:ptr1: first 1
cout << "ptr2: " << ptr2 << endl; //打印:ptr2: sencond 2
cout << "ptr3: " << ptr3 << endl; //打印:ptr3: sencond 2
cout << "" << endl;
cout << "3.不同对象之间的赋值" << endl;
ptr1 = ptr3; //打印:call: HasPtr(const HasPtr& p),
//HasPtr& HasPtr::operator=(HasPtr p)
//HasPtr::~HasPtr():first 1
//delete ps; delete use;
cout << "ptr1: " << ptr1 << endl; //打印:ptr1: sencond 3
cout << "ptr2: " << ptr2 << endl; //打印:ptr2: sencond 3
cout << "ptr3: " << ptr3 << endl; //打印:ptr3: sencond 3
cout << "" << endl;
cout << "4.指向类型相同赋值" << endl;
ptr1 = ptr3; //打印:call: HasPtr(const HasPtr& p)
//HasPtr& HasPtr::operator=(HasPtr p)
//HasPtr::~HasPtr():first 1
//delete ps; delete use;
cout << "ptr1: " << ptr1 << endl; //打印:ptr1: sencond 3
cout << "ptr2: " << ptr2 << endl; //打印:ptr2: sencond 3
cout << "ptr3: " << ptr3 << endl; //打印:ptr3: sencond 3
cout << "" << endl;
cout << "5.自符值" << endl;
ptr1 = ptr1; //打印:call: HasPtr(const HasPtr& p)
//HasPtr& HasPtr::operator=(HasPtr p)
//HasPtr::~HasPtr():sencond 4
cout << "ptr1: " << ptr1 << endl; //打印:ptr1: sencond 3
cout << "ptr2: " << ptr2 << endl; //打印:ptr2: sencond 3
cout << "ptr3: " << ptr3 << endl; //打印:ptr3: sencond 3
cout << "" << endl;
cout << "程序结束" << endl;
//打印:
//HasPtr::~HasPtr():sencond 3
//HasPtr::~HasPtr():sencond 2
//HasPtr::~HasPtr():sencond 1
//delete ps; delete use;
return 0;
}