c++ cv::Mat是否可以从其按位序列化形式安全地重新创建?

q5lcpyga  于 2023-01-28  发布在  其他
关注(0)|答案(1)|浏览(160)

考虑以下代码:

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,以避免内存释放,当删除m1m2时,并且refcount将下降到0。在作用域之间,不存在指向存储器中分配位置的实际cv::Mat,但是这种cv::Mat的副本存在于存储器中。
然后我试着从存储中反序列化它来恢复它。m3现在是已经消失的m1的副本,它指向仍然分配的内存块,并且refcount=1。所以我希望一切都能正常工作。但是当我们到达作用域的末尾,并且调用了m3的析构函数时,我得到了invalid pointer异常。这个设计有什么问题吗?

bkhjykvo

bkhjykvo1#

C++对象不仅仅是字节的集合。你可以can't按位复制一个任意的对象到另一个对象中,并假装你已经重新创建了原来的对象。你只能对普通的类型这样做,而cv::Mat不是这样的类型。所有关于防止矩阵元素缓冲区被释放的技巧都是无关紧要的。因为cv::Mat可能具有并且实际上具有阻止正确逐位移动的其它实现细节。
下面是遵循cv::Mat的这些细节的类型的简单示例:

struct S {
    int* a;    // = cv::Mat::step.p
    int  b;    // = cv::Mat::step.buf
 
    // other irrelevant data members including matrix elements buffer
 
    S() {
        a = &b;
    }
 
    ~S() {
        if (a != &b)
            delete a;
    }
};

std::vector<char> storage;
{
    S s1;
    auto ptr = reinterpret_cast<char*>(&s1);
    storage.insert(storage.end(), ptr, ptr + sizeof(S));
}
{
    S s2;
    char* ptr = reinterpret_cast<char*>(&s2);
    std::copy_n(storage.begin(), sizeof(S), ptr);
}

用按位序列化的s1按位覆盖s2的错误应该是非常明显的:你把s2.a设为s1.b,如果你幸运的把s1s2放在同一个地址,就不会有什么问题,如果它们在不同的地址,你就把delete设为栈中的一个变量,UB就是这样的:有时你的代码看起来可以工作,有时却崩溃了。
下面是一个完整的演示,您可以玩:https://godbolt.org/z/5ezcaWv7x

相关问题