我可以在使用 std::make_shared 后缓存吗?
Can I cache after using std::make_shared?
我正在阅读 Effective Modern C++ (Scott Meyers) 并尝试第 21 项中的内容。书中说使用 std::make_shared
的副作用是在所有 shared_ptr
之前无法释放内存并且 weak_ptr
s 不见了(因为控制块是和内存一起分配的)。
我预计这将意味着如果我保留一个缓存来容纳一堆 weak_ptr
,那么将永远不会释放任何内存。我使用下面的代码尝试了这个,但是当 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;
}
的确,当您使用 std::make_shared
时,新对象和控制块的存储被分配为单个块,因此只要存在 std::weak_ptr
给它。但是,当最后一个 std::shared_ptr
被销毁时,对象仍然被销毁(它的析构函数运行并且它的成员被销毁)。它只是关联的存储空间仍处于分配状态且未被占用。
std::vector
为其元素动态分配存储空间。此存储在 std::vector
外部,它不是对象内存表示的一部分。当您销毁 Widget
时,您也会销毁其 std::vector
成员。该成员的析构函数将释放用于存储其元素的动态分配的内存。唯一不能立即释放的内存是控制块和 Widget
的存储(应该是 sizeof(Widget)
字节)。它不会阻止立即释放向量元素的存储。
我正在阅读 Effective Modern C++ (Scott Meyers) 并尝试第 21 项中的内容。书中说使用 std::make_shared
的副作用是在所有 shared_ptr
之前无法释放内存并且 weak_ptr
s 不见了(因为控制块是和内存一起分配的)。
我预计这将意味着如果我保留一个缓存来容纳一堆 weak_ptr
,那么将永远不会释放任何内存。我使用下面的代码尝试了这个,但是当 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;
}
的确,当您使用 std::make_shared
时,新对象和控制块的存储被分配为单个块,因此只要存在 std::weak_ptr
给它。但是,当最后一个 std::shared_ptr
被销毁时,对象仍然被销毁(它的析构函数运行并且它的成员被销毁)。它只是关联的存储空间仍处于分配状态且未被占用。
std::vector
为其元素动态分配存储空间。此存储在 std::vector
外部,它不是对象内存表示的一部分。当您销毁 Widget
时,您也会销毁其 std::vector
成员。该成员的析构函数将释放用于存储其元素的动态分配的内存。唯一不能立即释放的内存是控制块和 Widget
的存储(应该是 sizeof(Widget)
字节)。它不会阻止立即释放向量元素的存储。