为什么我的新分配指针会在程序退出时自动删除?

时间:2016-06-23 10:24:41

标签: c++ pointers c++14

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

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的析构函数吗?

4 个答案:

答案 0 :(得分:4)

ytxt("class").Write(std::string((char*)&kar, sizeof(kar)));

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

程序中的每个其他问题都归结为具有正确数据的字节垃圾。

答案 1 :(得分:3)

即使您修复了析构函数,此代码也无法正常工作:存储包含指针的对象的字节,然后尝试将该对象恢复到另一个内存中是不正确的。

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

从文件中恢复lol的实例时,其指针指向已由kar的析构函数解除分配的内存块。这是未定义的行为。

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

注意:将类放入分配为字节数组的内存中的情况需要 placement new运算符。有关如何在放置新内容后进行清理的信息,请参阅this Q&A

答案 2 :(得分:2)

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

答案 3 :(得分:1)

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

结构或类(除了默认的public vs private之外),除了pod,没有虚拟,没有析构函数,没有构造函数,内置类型(如int或者指针和数组)之外什么都不包含旧数据。

将这些作为比特流读取/写入至少做了一些明智的事情。

如果它包含指针,指向的东西的地址将在程序重启后继续存在,所以请避免使用。

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

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

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

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

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