巧妙地使用shared_ptr

猪小花1号2018-08-28 15:37

情景:

假设你在数据库存了一份资源(这份资源也许用完后就要删除或者需要做些其他操作),并且你有多处需要读取这个资源,但是你可能不太想每次都去数据库去取一遍。此时你可能需要先读取一份到内存中,然后大家共享这份资源。那么资源放在哪里,由谁持有,由谁释放并删除数据库数据就成为了一个问题。

带着这个问题,我们来看看下面的代码:

// 这里定义一个model,假设这就是我们需要的数据
struct Model {
...
};
// 定义一个需要这份数据的对象
//1. 理论上我们可以这样
class Window {
Window(const Model& model) {
model_ = model;
};
...
private:
Model model;
...
};

// 2.或者这样
class Window {
Window(std::unique_ptr<Model> model) {
model_ = model;
};
...
private:
std::unique_ptr<Model> model;
...
};

// 3.或者这样
class Window{
Window(std::shared_ptr<Model> model) {
model_ = model;
};
...
private:
std::shared_ptr<Model> model_;
...
};

我们可能需要创建这么几个对象


  
  
  1. Window win1(...);
  2. Window win2(...);
  3. Window win3(...);

那么假设使用第一种形式,当对象win1,win2,win3都需要对其进行引用时内存中就会有三份数据,这显然是不合理的,尽管现如今内存并没有以前那么紧张,但是我们要做一个有修养的程序员。第二种显然也是不可以的,如果考虑到对象之间的数据是可以传递的,那还不如第一种方案,尽管人家占点内存,但至少人家是可以拷贝传递的。 
很好,现在只剩最后一种方案了,可能你会说,这样使用shared_ptr无非是让内存占用更小,更易于数据传递。那么你要注意了,假设我们像下面这样定义一个对象持有该份数据:

class ModelHolder {
ModelHolder () {
// load model
};
~ModelHolder () {
// delete model or do something alse
};
Model GetModel() {
return model_;
};
private:
Model model_;
} ;

// 定义对象
class Window {
...
private:
std::shared_ptr<ModelHolder> model_holder_;
...
};
std::shared_ptr<ModelHolder> model_holder =
std::make_shared<ModelHolder>();
Window win1(model_holder);
Window win2(model_holder);
Window win3(model_holder);

那么显然当model_holder创建时,数据加载一次,之后所有人共享一份数据,当所有持有者全部析构的时候 model_holder也将因引用计数为0而触发析构,此时可在该对象的析构函数中完成相关数据处理逻辑。简单的说:就是将数据交由一个shared_ptr对象持有,其他人则持有该对象,当全部持有者析构的时候该shared_ptr也将随之析构,此时由该对象的析构函数处理数据的释放及删除等操作是最合适不过的。以上描述基本可以简单的概括为下图: 




网易云新用户大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者李健授权发布。