【问题标题】:unique_ptr read access violation when losing scope丢失范围时的 unique_ptr 读取访问冲突
【发布时间】:2019-10-04 08:41:11
【问题描述】:

我正在使用 std::ifstream 从二进制文件中读取 char 数组,然后将 char 数组重新解释为指向我的结构的指针,然后将其设为 unique_ptr。 一切正常,除非 unique_ptr 超出范围时,我会遇到读取访问冲突。

我做错了吗?我不确定为什么会发生错误。我在该数据上只有一个 unique_ptr。

我已经包含了产生错误的非常基本的代码版本。

struct mystruct {
    int val = 0;
    double val2 = 5.6;
    std::string string = "test";

};

int main()
{

    //brackets here just to encapsulate the scope for this example
    {
        //try to open the stream for reading
        std::ifstream stream;
        stream.open("test.bin", std::ios::binary | std::ios::in);

        char* buffer = new char[sizeof(mystruct)];
        stream.read(buffer, sizeof(mystruct));

        mystruct * pointer = reinterpret_cast<mystruct*>(buffer);
        std::unique_ptr<mystruct> obj = std::unique_ptr<mystruct>(pointer);

        //ha no problem reading the struct data
        std::cout << "read back: " << obj->string << endl;

        stream.close();
    }

    //obj goes out of scope and throws a read access violation
}

我希望 unique_ptr 只是删除对象,不会抛出任何错误

**********编辑************************
感谢 cmets 和答案 - 基本上是在您的帮助下,我生成了我试图这样做的代码,并在此处列出了它,以防它对其他人有所帮助。
要点是:
* 如果从二进制读取和写入,则不建议在结构中使用 std::string,因为 std::string 的字节数未知。
* 需要在分配指针之前在内存中创建对象 - std::make_unique() 适合于此。

struct mystruct {
    int val1 = 0;
    double val2 = 5.6;
    char somestring[10] = "string";

};

int main()
{

    //brackets here just to encapsulate the scope for this example
    {
        //try to open the stream for reading
        std::ifstream stream;
        stream.open("test.bin", std::ios::binary | std::ios::in);

        //hold the results in a vector
        auto results = std::vector<std::unique_ptr<mystruct>>();

        //read a vectory or mystructs from the binary file
        while (!stream.eof())
        {
            //create the object - NOTE: make_unique initialises my struct
            std::unique_ptr<mystruct> obj = std::make_unique<mystruct>();

            //read from binary file into obj
            if (!stream.read(reinterpret_cast<char*>(obj.get()), sizeof mystruct))
                break;

            //add the obj to th vector
            results.push_back(std::move(obj));
        }

        stream.close();

        for (auto& val : results)
        {
            cout << "read back: " << val->somestring << endl;
        }


    }
}

【问题讨论】:

  • 你的程序表现出未定义的行为,通过将一个与new分配的类型不同的指针传递给delete。实际上,它在obj-&gt;string 的早期表现出未定义的行为,通过在对象的生命周期开始之前访问它;你只是碰巧侥幸逃脱(可能要感谢小字符串优化)。
  • 有没有更好的方法将二进制文件读入具有 unique_ptr 的结构?
  • 假设没有特定于类的operator new,使用::operator new 进行原始分配,然后使用placement new。
  • 你的结构包含一个std::string,它不是POD,所以用fstream::write()fstream::read()写和读是行不通的。
  • 但是你绝对不能从文件中读取std::string,这没有意义。

标签: c++ ifstream unique-ptr


【解决方案1】:

您的代码中有 3 种未定义的行为。

首先,你假装在一个没有对象的地方有一个对象。你从未创建过mystruct;您刚刚分配了一些内存字节。仅仅创建reinterpret_cast 不足以创建mystruct。所以任何使用pointer 访问不存在的“对象”的都是UB。

其次,即使该缓冲区中有一个,mystruct 也不能轻易复制,因此您不能只是复制它的字节。您不能将一堆字节读入一个不可复制的对象。正是因为存在一个不可复制的非静态数据成员(即:mystruct::string),才使其不可复制。

第三,你尝试删除这个mystruct。但是没有mystruct,你正在删除一个不存在的东西。从技术上讲,这可能已包含在 #1 中,但这很可能是导致您的代码彻底崩溃的原因。

如果你知道为什么“读取结构数据没问题”碰巧起作用了,那么std::string 实现使用小字符串优化的可能性很大,如果它足够小,它会将字符串存储在 std::string 本身中.对于小字符串,按字节进行复制可能足够接近“工作”以允许您读取字符串数据。

但这只是幸运。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-09
    • 2019-09-17
    • 2019-05-25
    相关资源
    最近更新 更多