考虑以下代码:
cv::Mat currentFrame; // some proper frame with allocated memory
std::vector<uint8_t> storage;
{
cv::Mat m1 = currentFrame.clone();
cv::Mat m2 = m1;
const auto *m1ptr = reinterpret_cast<const uint8_t *>(&m1);
storage.insert(storage.end(), m1ptr, m1ptr + sizeof(cv::Mat));
m1.addref();
}
{
cv::Mat m3;
uint8_t *dstPtr = reinterpret_cast<uint8_t *>(&m3);
std::copy_n(storage.begin(), sizeof(cv::Mat), dstPtr);
}
在第一个作用域中,我使用cv::Mat
映像,进行一些复制以增加引用计数,最后只按位串行化cv::Mat
头文件(96字节)到向量storage
中。注意m1.addref()
,其中我将refcount增加1,以避免内存释放,当删除m1
和m2
时,并且refcount将下降到0。在作用域之间,不存在指向存储器中分配位置的实际cv::Mat
,但是这种cv::Mat
的副本存在于存储器中。
然后我试着从存储中反序列化它来恢复它。m3
现在是已经消失的m1
的副本,它指向仍然分配的内存块,并且refcount=1。所以我希望一切都能正常工作。但是当我们到达作用域的末尾,并且调用了m3
的析构函数时,我得到了invalid pointer异常。这个设计有什么问题吗?
1条答案
按热度按时间bkhjykvo1#
C++对象不仅仅是字节的集合。你可以can't按位复制一个任意的对象到另一个对象中,并假装你已经重新创建了原来的对象。你只能对普通的类型这样做,而
cv::Mat
不是这样的类型。所有关于防止矩阵元素缓冲区被释放的技巧都是无关紧要的。因为cv::Mat
可能具有并且实际上具有阻止正确逐位移动的其它实现细节。下面是遵循
cv::Mat
的这些细节的类型的简单示例:用按位序列化的
s1
按位覆盖s2
的错误应该是非常明显的:你把s2.a
设为s1.b
,如果你幸运的把s1
和s2
放在同一个地址,就不会有什么问题,如果它们在不同的地址,你就把delete
设为栈中的一个变量,UB就是这样的:有时你的代码看起来可以工作,有时却崩溃了。下面是一个完整的演示,您可以玩:https://godbolt.org/z/5ezcaWv7x