假设我们有以下场景。
我们有一个ImageManager
类,用于内部存储和管理图像数据。
ImageManager
类有一个公共(public)成员 populateImage
,它将把图像读入内存,然后返回一个填充的 MyImage
,它是一个 std::shared_ptr
围绕 Image
对象。该对象将包含一个uuid
,它映射到由ImageManager
管理的图像数据。
最后,我们定义一个回调函数,该函数在 Image
析构函数中调用,并正确清理 ImageManager
存储的图像数据。
示例如下:
#include <functional>
#include <vector>
#include <unordered_map>
#include <memory>
class Image {
public:
Image() = default;
~Image(){
if (m_deleter) {
m_deleter();
}
}
private:
friend class ImageManager;
std::function<void()> m_deleter = nullptr;
unsigned int m_uuid;
};
using MyImage = std::shared_ptr<Image>;
class ImageManager {
public:
void removeImage(unsigned int uuid) {
auto iter = m_imageMap.find(uuid);
if (iter == m_imageMap.end()) {
throw std::runtime_error("Unable to find image for UUID: " + std::to_string(uuid));
}
m_imageMap.erase(iter);
}
void populateImage(MyImage& image) {
image = std::make_shared<Image>();
static unsigned int uuid = 0;
++uuid;
image->m_uuid = uuid;
auto img = std::vector<uint8_t>(1000, 0);
m_imageMap[uuid] = img;
unsigned int currentUUID = uuid;
auto callbackFunc = [this, currentUUID]() {
this->removeImage(currentUUID);
};
image->m_deleter = callbackFunc;
}
private:
std::unordered_map<unsigned int, std::vector<uint8_t>> m_imageMap;
};
当我们的 ImageManager
实例在 Image
实例之前超出范围时,就会出现问题,例如在以下驱动程序代码中:
int main() {
MyImage img1, img2;
{
ImageManager manager;
manager.populateImage(img1);
manager.populateImage(img2);
}
}
运行该程序打印:
terminate called after throwing an instance of 'std::runtime_error'
what(): Unable to find image for UUID: 2
但最终我明白这是未定义的行为,因为 m_deleter
中的 this
指针不再指向有效的对象。
为了避免这个问题,正确的设计模式是什么?
最佳答案
总体来说,设计有味道。如果ImageManager
是本地对象,而MyImage
在范围之外,为什么需要删除 map 中的项目?
无论如何,我答应你展示共享/弱的习语。将 map 包装到 shared_ptr
中。这意味着 map 将与 ImageManager 一起被销毁(如果您不复制此共享指针):
std::shared_ptr<std::unordered_map<unsigned int, cv::Mat>> m_imageMap;
在 lambda 中存储弱指针:
std::weak_ptr<std::unordered_map<unsigned int, cv::Mat>> weakPtr = m_imageMap;
auto callbackFunc = [weakPtr, uuid]() {
auto imageMap = weakPtr.lock();
if (imageMap)
auto iter = imageMap.find(uuid);
if (iter == imageMap.end()) {
throw std::runtime_error("Unable to find image for UUID: " + std::to_string(uuid));
}
imageMap.erase(iter);
}
};
image->m_deleter = callbackFunc;
弱指针会知道对应的共享指针是否被销毁。
请记住,在这种情况下不应使用 std::make_shared
:否则只有当最后一个图像被销毁时,与 map 关联的内存才会被释放。
关于c++ - 析构函数中使用可能指向或不指向有效对象的指针的回调的正确设计模式是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71042377/