【问题标题】:Why is my new allocated pointer automatically deleted on program exit?为什么我的新分配的指针在程序退出时会自动删除?
【发布时间】:2016-10-25 15:06:42
【问题描述】:

我有以下类,我以二进制模式保存在文件中:

class lol
{
public:
    ~lol() {
        std::cout << "destucted" << std::endl;
        system("pause");
    }

    int age;
    std::string name;
};

现在我主要有这个:

int main()
{
    {
        lol kar;
        kar.age = 17;
        kar.name = "Blabla";

        ytxt("class").Write(std::string((char*)&kar, sizeof(kar))); //Saves the class in binary mode
    }

    lol * asd;

    {
        std::string stClass = ytxt("class").Read();//reads the file in binary
        asd = (lol*)new char[stClass.length()];//asd is now allocated with new
        memcpy_s(asd, stClass.length(), stClass.c_str(), stClass.length());//copy the content of the file into the class
    }

    std::cout << "Name: " << asd->name << std::endl;//output
    std::cout << "Age: " << asd->age << std::endl;

    asd->age = 79;
    std::cout << "Age: " << asd->age << std::endl;

    delete asd;//I cannot delete it as lol
    //delete (char*)asd;//works but lol destructor is not called
    system("pause");
}

asd 指针的内容正在打印到控制台,指针被删除并调用析构函数。最后抛出异常。

当我不删除 asd 指针时,一切正常,但不调用 asd 指针的析构函数。我知道指针被分配为new char,我必须将它作为new char 删除,但是还有另一种方法可以让删除操作调用lol 的析构函数吗?

【问题讨论】:

  • 什么是ytxt。它是否正确序列化字符串?或者只是转储指针。
  • 为什么将对象创建为new char[stClass.length()] 而不是new lol()
  • 这将最终加入数百个类似问题的行列,其中有人试图简单地对非 POD 类型进行 blob 管理。可悲的是,这样做的尝试在措辞、标题和代码示例上大相径庭,几乎不可能在这数百个重复项中找到一个。

标签: c++ pointers c++14


【解决方案1】:
ytxt("class").Write(std::string((char*)&kar, sizeof(kar)));

这一行确实保存了一个字节转储。但是您无法加载该字节转储并期望它再次成为您的类的正确实例。这不是二进制序列化的工作方式。这仅适用于 POD 类型(也可能不适用于该确切行)。如果您想保存数据并稍后加载,则需要另一种方法。

程序中的所有其他问题都归结为正确数据应该存在的字节垃圾。

【讨论】:

    【解决方案2】:

    即使你修复了你的析构函数,这段代码也没有机会工作:存储包含指针的对象的字节,然后尝试将该对象恢复到另一个内存中是不正确的。

    您的lol 类有std::string,它存储了指向字符串实际字节的指针。当您将 kar 的字节复制到文件中时,您存储的是正在运行的程序的指针,而不是存储字符串本身的内容。

    当您从文件中恢复lol 的实例时,它的指针指向一块已被kar 的析构函数释放的内存。这是未定义的行为。

    您需要更改序列化对象的方法。您目前的方法无法挽救。

    注意:当您将类放入分配为字节数组的内存时,需要 placement new 运算符。请参阅this Q&A,了解如何在放置新位置后进行清理。

    【讨论】:

    • 基本上我需要用静态char 数组替换std::string,并用删除指针之前需要调用的函数替换析构函数(如果我想要析构函数)。对吗?
    • @terrakuh 如果您可以灵活地进行此更改,那么它可以在没有构造函数/析构函数的情况下工作。
    【解决方案3】:

    由于你的 lol 类包含一个 std::string,它是一个复杂的对象并且有它的内部内存管理,当它实际上不是一个真实的对象而是文件中的一些随机字节时试图删除它,将导致内存损坏/崩溃。

    【讨论】:

      【解决方案4】:

      只有一些 C++ 结构可以作为原始字节进行写入和读取,并以合理的方式工作。以这种方式工作的那种称为“普通旧数据”,或简称 pod。 (在 C++11 中,他们将其分解为更细粒度的概念,但这是一个细节。)

      一个结构或类(除了默认的公共和私有之外的东西)只包含 pod、没有虚拟、没有析构函数、没有构造函数和内置类型(如 int 或指向相同的指针和数组)是普通的旧数据.

      以比特流的形式读取/写入它们至少可以做到一半明智。

      如果它包含指针,则指向的东西的地址在程序重新启动后将无法生存,因此请避免。

      std::string 不符合条件,包含它的任何内容也不符合条件。

      要从文件中保存/加载,您应该查看序列化问题。有很多很多的技术,但没有一个是免费的。

      为某些输出存档类型put_here 定义一个免费的save( put_here, X const&amp; _ 函数很简单。然后为您想要支持的每种类型创建重载。对load 执行相同操作。对于复合类型,只需保存/加载每个元素。

      为版本控制添加标签等在非平凡代码中也很有用。

      对于像字符串这样的序列容器,可以保存为save(here, str.size()); for (auto&amp;&amp;c:str)save(here, c);

      【讨论】:

        猜你喜欢
        • 2021-04-08
        • 2014-01-04
        • 1970-01-01
        • 2022-01-10
        • 2020-11-13
        • 2020-11-18
        • 2017-12-25
        • 2017-05-02
        • 2017-12-21
        相关资源
        最近更新 更多