首页 > 解决方案 > 使用 std::make_shared 后可以缓存吗?

问题描述

我正在阅读 Effective Modern C++ (Scott Meyers) 并尝试第 21 项中的一些内容。这本书说使用的副作用是在所有s 和s 都消失std::make_shared之前无法释放内存(因为控制块与内存一起分配)。shared_ptrweak_ptr

我预计这将意味着如果我保留一个缓存来保存一堆weak_ptrs,那么就不会释放任何内存。我使用下面的代码进行了尝试,但是由于从向量中删除了 shared_ptrs,我可以使用 pmap 看到实际上正在释放内存。谁能解释我哪里出错了?或者如果我的理解是错误的?

注:loadWidget本实验的目的与书中的功能不同。

#include <iostream>
#include <memory>
#include <unordered_map>
#include <vector>
#include <thread>
#include <chrono>

class Widget {
public:
  Widget()
    : values(1024*1024, 3.14)
  { }

  std::vector<double> values;
};

std::shared_ptr<Widget> loadWidget(unsigned id) {
  return std::make_shared<Widget>();
}

std::unordered_map<unsigned, std::weak_ptr<Widget>> cache;

std::shared_ptr<Widget> fastLoadWidget(unsigned id) {
  auto objPtr = cache[id].lock();

  if (!objPtr) {
    objPtr = loadWidget(id);
    cache[id] = objPtr;
  }

  return objPtr;
}

int main() {
  std::vector<std::shared_ptr<Widget>> widgets;
  for (unsigned i=0; i < 20; i++) {
    std::cout << "Adding widget " << i << std::endl;
    widgets.push_back(fastLoadWidget(i));

    std::this_thread::sleep_for(std::chrono::milliseconds(500));
  }
  while (!widgets.empty()) {
    widgets.pop_back();

    std::this_thread::sleep_for(std::chrono::milliseconds(500));
  }
  
  
  return 0;
}

标签: c++effective-c++

解决方案


确实,当您std::make_shared将存储用于新对象和控制块时,它是作为单个块分配的,因此只要存在它就不会释放std::weak_ptr它。但是,当最后一个std::shared_ptr对象被销毁时,对象仍然被销毁(它的析构函数运行并且其成员被销毁)。它只是保持分配和未占用的关联存储。

std::vector为其元素动态分配存储空间。此存储在 外部std::vector,它不是对象的内存表示的一部分。当你摧毁一个时,Widget你也摧毁了它的std::vector成员。该成员的析构函数将释放用于存储其元素的动态分配的内存。唯一不能立即释放的内存是控制块和存储Widget(应该是sizeof(Widget)字节)。它不会阻止向量元素的存储立即释放。


推荐阅读