析构函数中使用可能指向或不指向有效对象的指针的回调的正确设计模式是什么?

2024-01-12

假设我们有以下场景。

我们有一个ImageManager类用于内部存储和管理图像数据。 这ImageManager类有一个公共成员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

但最终我明白这是未定义的行为this指针指向m_deleter不再指向有效的对象。

为了避免这个问题,正确的设计模式是什么?


整体设计有味道。如果ImageManager是一个本地对象,并且MyImage超出了范围,为什么需要删除地图中的项目?

不管怎样,我答应你展示共享/弱的习语。将地图包裹成shared_ptr。这意味着地图将与 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在这种情况下:否则,只有当最后一个图像被销毁时,与地图关联的内存才会被释放。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

析构函数中使用可能指向或不指向有效对象的指针的回调的正确设计模式是什么? 的相关文章

随机推荐