Shared_ptr使用
编写C++程序中最头疼的事情莫过于内存的管理,有时候很羡慕隔壁写C#的,只需要关心功能是否实现,当然现在也有很多C++内存泄露的检测工具,等对STL,boost有了一定的了解,要开始去了解那部分知识。
裸指针固然效率很高,但是如果不够严谨的话,容易出现问题。不好管理的地方:
- 创建对象,函数中有if或者switch语句,在异常处理的时候直接return,忘记释放
- 需要多处调用同一个对象,因为涉及多个函数,最后你已经忘记了释放。
Shared_ptr有点类似内核对象,使用引用计数,当计数为0,释放资源。以引用计数方式实现资源共享。Ok,让我们看看shared_ptr简单例子,借用网上一个小例子,忘记出处了。
class A
{
public:
A(int data):num(data)
{
cout << "get new a" << endl;
}
A(const A &other)
{
num = other.num;
cout << "copy" << endl;
}
int getNum()
{
return num;
}
~A()
{
cout << num << " destroy" << endl;
}
public:
int num;
};
int main()
{
boost::shared_ptr<A> data(new A(3));
//建议使用下面这种方式
// boost::shared_ptr<A>data=boost::make_shared<A>(3);
cout<<data->getNum()<<endl;
return 0;
}
输出:
get new a
3
3 destroy
使用shared_ptr,希望不再使用new,但是我们看到很奇怪的是,有一个new,delete由shared_ptr自动释放。而使用工厂方法的make_shared不再需要new,且性能更高,make_shared只需要申请一次内存,包括数据与内存控制块,而shared_ptr先申请内存,再申请控制块,见make_shared和shared_ptr的区别。
实际情况中往往不是简单的一个类,比如与容器的结合。
class A_Mgr
{
public:
int size;
typedef boost::shared_ptr<A> DataPtr;
std::vector<DataPtr> vsa;
public:
A_Mgr()
{
size = 0;
}
~A_Mgr()
{
cout << "size is:" << vsa.size() << endl;
}
void insert_data(int data)
{
vsa.push_back(boost::shared_ptr<A>(new A(data)));
}
void insert_data(A *data)
{
vsa.push_back(boost::shared_ptr<A>(data));
}
void insert_data(boost::shared_ptr<A> data)
{
cout << "before insert ,data size is" << data.use_count() << endl;
vsa.push_back(data);
}
// int insert_data()
// {
// // boost::shared_ptr<A> ele=make_shared<A>();
// vsa.push_back(boost::make_shared<A>());
// return size++;
// }
DataPtr operator[](int index)
{
assert(index<size);
cout << "use count is:" << vsa[index].use_count() << endl;
return vsa[index];
}
void clear()
{
cout << "use count is:" << vsa[0].use_count() << endl;
size = 0;
vsa.clear();
}
};
以vector为例,存放share_ptr的数据,本例子中使用3种不同的方式插入数据
2)传入指针的方式,意味着,在A_Mgr外面有一个已经创建好的指针,new了对象。如果万一忘记了,我们手动使用了delete,。。。
3)直接传入shared_ptr显然也是我们不希望看到的方式,因为这种方式存入,资源在创建对象的函数中还有一个计数。
因为实际需要,重载了一个[]操作符,使用拷贝构造函数主要有这么几种方式:
- 函数以传值方式
- 函数返回值为值
- 以一个对象初始化另一个对象
引用计数的增加也在这3个地方,因此,这里的[]操作符返回,切记不要再用对象来接了,否则生命周期将受这个对象的生存周期影响。
顺便测试了下vector,这里的clear将会正确的释放A的内存。在使用vector,clear之后使用swap,shrink_to_fit都能够减少vector的大小。