C++17中从std::queue获取含cv::Mat的FRAME结构体是否正确?
问题描述
我尝试用std::queue和OpenCV实现一个IP摄像头帧抓取类,最多缓存10帧供其他类获取。相关代码如下:
/* Camera.cpp */ struct FRAME { cv::Mat Img; uint64_t Time; bool ReadError; }; class Camera { std::queue<FRAME> m_buffer; std::mutex m_buffer_mutex; ... }; void Camera::threadFunc_get_stream_from_camera() { while (true) { FRAME frame; ... // 读取帧的逻辑(如果cv::read失败则跳过存入缓存的步骤) m_buffer_mutex.lock(); while (m_buffer.size() >= 10) m_buffer.pop(); m_buffer.push(frame); m_buffer_mutex.unlock(); } } FRAME Camera::grab() { std::unique_lock ulock(m_buffer_mutex); FRAME frame; if (m_buffer.empty() == false) { frame = m_buffer.front(); m_buffer.pop(); return frame; } } /***************/ /* Testing.cpp */ int main() { // Camera* cam .. ... while (true) { auto frame = cam->grab(); if (frame.Img.data == nullptr || frame.Time == 0) continue; ... // 帧图像处理逻辑 } }
程序稳定运行了约3天,没有异常或内存问题,但我有个疑问:grab()函数返回非指针类型的FRAME是否正确?我用的是C++17,知道结构体里的uint64_t和bool拷贝没问题,但不确定cv::Mat的情况。另外我不想用生产者-消费者模式,因为不想让Camera类依赖测试类的代码。
解答
1. 返回非指针类型FRAME是完全正确的
你担心的cv::Mat拷贝问题其实不用多虑:
cv::Mat内部采用引用计数机制,默认的拷贝构造/赋值操作都是浅拷贝,只会复制矩阵头(包含指向像素数据的指针、尺寸、类型等信息),不会复制实际的像素内存,拷贝成本极低。- 当最后一个持有该数据的
cv::Mat对象被销毁时,才会自动释放像素内存,不会出现内存泄漏。 - C++17对结构体的拷贝语义没有特殊限制,
FRAME里的uint64_t、bool都是基本类型,拷贝行为完全可靠。
2. 当前代码的潜在小问题
虽然运行稳定,但有几个细节可以优化:
grab()函数在缓存为空时,会返回一个默认构造的FRAME(Img.data为nullptr、Time为0),这和你测试代码里的判断逻辑匹配,但最好在函数里明确处理这种情况,比如添加注释或者保证返回值的一致性。- 用
std::lock_guard替代手动调用lock()/unlock()更安全:在threadFunc_get_stream_from_camera()中,如果push()之前抛出异常,手动解锁的代码可能不会执行,而std::lock_guard会在作用域结束时自动释放锁,避免死锁风险。修改后的生产者逻辑:void Camera::threadFunc_get_stream_from_camera() { while (true) { FRAME frame; ... // 读取帧的逻辑 std::lock_guard<std::mutex> lock(m_buffer_mutex); while (m_buffer.size() >= 10) m_buffer.pop(); m_buffer.push(frame); } }
3. 关于生产者-消费者模式的顾虑
你的顾虑是合理的——不需要让Camera类依赖业务逻辑(测试类)。当前的“生产者主动填充缓存,消费者按需抓取”的模式完全没问题,不属于需要耦合业务的典型生产者-消费者实现。如果后续需要更灵活的控制(比如缓存满时阻塞生产者),可以考虑用std::condition_variable,但这也不需要让Camera类依赖测试类,只需要在Camera内部封装条件变量的逻辑即可。
内容的提问来源于stack exchange,提问作者mystes
相关产品推荐
相关产品推荐

