You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++自定义对象反序列化后析构触发访问违规错误的排查与解决

C++自定义序列化器反序列化后访问违规问题排查与解决

问题描述

编写C自定义对象序列化器时,序列化过程无异常,但反序列化后,在程序关闭或对象清理阶段触发访问违规错误。运行平台为Win10,所有C版本均出现相同问题,改用指针版本也无法解决。

错误信息

错误1:

Exception thrown at 0x00007FF790BFADED in app.exe: 0xC0000005: Access violation reading location 0x000001E4E61DBED0

错误2:

Exception thrown: read access violation. _Pnext was 0xFFFFFFFFFFFFFFFF

相关代码

序列化器头文件(ObjectSerialization.h)

#ifndef OBJ_SER_API_H
#define OBJ_SER_API_H

#include <fstream>

class ObjectSerialization
{
public:
    /// <summary>
    /// Serialize the provided object to a binary file.
    /// </summary>
    /// <param name="filePath">The path to the binary file where the object will be serialized.</param>
    /// <param name="objectToWrite">The object to be serialized.</param>
    template<typename T>
    static void Serialize(const wchar_t* filePath, const T& objectToWrite)
    {
        std::ofstream file(filePath, std::ios::binary);
        file.write(reinterpret_cast<const char*>(&objectToWrite), sizeof(objectToWrite));
        file.close();
    }

    /// <summary>
    /// Deserialize an object from a binary file.
    /// </summary>
    /// <param name="filePath">The path to the binary file from which the object will be deserialized.</param>
    /// <returns>The deserialized object.</returns>
    template<typename T>
    static T Deserialize(const wchar_t* filePath)
    {
        T object;
        std::ifstream file(filePath, std::ios::binary);
        file.read(reinterpret_cast<char*>(&object), sizeof(object));
        file.close();
        return object;
    }
};

#endif // !OBJ_SER_API_H

主函数代码

#include <iostream>
#include <string>
#include "ObjectSerialization.h"

class Student {
public:
    std::wstring Name;
    int Age = 0;
};

int main()
{
    // Serialization
    {
        Student student;
        student.Name = L"Michael";
        student.Age = 22;
        
        ObjectSerialization::Serialize<Student>(L"Student.dat", student);
    }

    // Deserialization
    {
        Student student = ObjectSerialization::Deserialize<Student>(L"Student.dat");
        std::wcout << student.Name << std::endl;
        std::cout << student.Age << std::endl;
    } // <- exception occurred


    std::cin.get();
    return 0;
}

问题根源

你的序列化方式是直接对对象进行内存二进制拷贝,这种方式仅适用于平凡类型(Trivial Type)——即没有自定义构造/析构函数、没有非平凡成员变量、内部不持有堆内存指针的类型。

但Student类包含std::wstring,这是一个非平凡类型:它内部维护着指向堆内存的指针,以及字符串长度、容量等状态。序列化时,你只是把这些指针值、长度、容量等内存数据写入了文件;反序列化时,这些指针指向的内存地址在当前进程中是无效的(原进程的堆内存已释放,或当前进程的该地址不属于你的程序)。当Student对象销毁时,std::wstring的析构函数尝试释放这个无效指针指向的内存,就触发了访问违规错误。

解决方案

方案1:为自定义类型实现成员级序列化/反序列化

修改序列化器,针对非平凡类型(如std::wstring)单独处理,逐个成员完成序列化与反序列化:

修改后的序列化器头文件

#ifndef OBJ_SER_API_H
#define OBJ_SER_API_H

#include <fstream>
#include <string>

// 提前声明Student类,用于模板特化
class Student;

class ObjectSerialization
{
public:
    // 序列化Student类型(模板特化)
    template<>
    static void Serialize<Student>(const wchar_t* filePath, const Student& student)
    {
        std::ofstream file(filePath, std::ios::binary);
        // 先写入Age
        file.write(reinterpret_cast<const char*>(&student.Age), sizeof(student.Age));
        // 写入Name的长度和数据
        size_t nameLen = student.Name.size();
        file.write(reinterpret_cast<const char*>(&nameLen), sizeof(nameLen));
        file.write(reinterpret_cast<const char*>(student.Name.data()), nameLen * sizeof(wchar_t));
        file.close();
    }

    // 反序列化Student类型(模板特化)
    template<>
    static Student Deserialize<Student>(const wchar_t* filePath)
    {
        Student student;
        std::ifstream file(filePath, std::ios::binary);
        // 读取Age
        file.read(reinterpret_cast<char*>(&student.Age), sizeof(student.Age));
        // 读取Name的长度和数据
        size_t nameLen = 0;
        file.read(reinterpret_cast<char*>(&nameLen), sizeof(nameLen));
        student.Name.resize(nameLen);
        file.read(reinterpret_cast<char*>(student.Name.data()), nameLen * sizeof(wchar_t));
        file.close();
        return student;
    }
};

#endif // !OBJ_SER_API_H

修改后的主函数

#include <iostream>
#include <string>
#include "ObjectSerialization.h"

class Student {
public:
    std::wstring Name;
    int Age = 0;
};

// 显式实例化模板特化,避免链接错误
template void ObjectSerialization::Serialize<Student>(const wchar_t*, const Student&);
template Student ObjectSerialization::Deserialize<Student>(const wchar_t*);

int main()
{
    // Serialization
    {
        Student student;
        student.Name = L"Michael";
        student.Age = 22;
        
        ObjectSerialization::Serialize<Student>(L"Student.dat", student);
    }

    // Deserialization
    {
        Student student = ObjectSerialization::Deserialize<Student>(L"Student.dat");
        std::wcout << student.Name << std::endl;
        std::cout << student.Age << std::endl;
    }

    std::cin.get();
    return 0;
}

方案2:使用通用序列化框架

如果需要序列化大量复杂类型,可以使用成熟的C++序列化库,比如Boost.Serialization、Protobuf等,这些库会自动处理非平凡类型的序列化逻辑,避免手动实现的繁琐。


内容的提问来源于stack exchange,提问作者Koray Üstündağ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 12:24:53