2012-02-24 69 views
1

我写了一个非常小的代码段,并已经得到了以下错误:SIGABRT二进制读/写

malloc: *** error for object 0x100100080: pointer being freed was not allocated

问题是,我不知道指针编译器的谈论。我通过地址传递一个变量到读/写函数,但我从来没有释放它,据我所知。我的代码中的错误在哪里?我用泄漏和僵尸跑了它,但什么也没有。

这是我的计划:

#include <iostream> 
#include <fstream> 
#include <string> 
#include <vector> 
#include <algorithm> 
using namespace std; 

class Bank 
{ 
private: 
    string __name; 

public: 
    Bank() 
    { 
     __name = ""; 
    } 
    Bank(string name) 
    { 
     __name = name; 
    } 

    string getName() const { return __name; } 
}; 


int main (int argc, char * const argv[]) 
{ 
    Bank bank("Bank of America"); 
    Bank bank2; 

    cout << "Bank1: " << bank.getName() << endl; 
    string filename = bank.getName() + ".bank"; 

    ofstream fout(filename.c_str(), ios::binary); 
    if (fout.good()) 
     fout.write((char *)&bank, sizeof(bank)); 
    fout.close(); 

    ifstream fin(filename.c_str(), ios::binary); 
    if (fin.good()) 
     fin.read((char *)&bank2, sizeof(bank2)); 
    fin.close(); 

    cout << "Bank2: " << bank2.getName() << endl; 

    return 0; 
} 
+2

不要使用标识符'__like_this'也不'_Like_this',他们保留的实现。 – Fanael 2012-02-24 15:45:39

回答

1

因为银行类包含的std :: string,你不能读/写二进制像你所想。 std :: string有内部指针。如果你把它写成二进制,你只会写指针而不是实际的字符串内容。同样,当你读取字符串时,你将会读取一个指针。在这种情况下,你最终会让银行和bank2对象都有指向相同内存的字符串,所以当内存被释放时它会被释放两次。

您需要有其他方式将银行数据写入文件。在这种情况下,使用银行名称的简单ASCII文件应该没问题。

+0

然而,后来它将有多个字段,例如'vector '来存储'Account'类型的对象(稍后定义)。二进制文件看起来更容易工作,这样我就可以将数据作为一个整体读取,而不是解析它。 – rcplusplus 2012-02-24 16:17:39

+0

不幸的是,没有任何内置于C++的东西可以让你轻松做到。有一些库如boost :: serialization试图使它更容易,但是如果你改变了你的类,你仍然需要修改你的序列化代码。 – 2012-02-24 16:22:09

1

你不能做你正在做的事情,只是因为std::string不能像那样被复制。在内部,一个string对象会分配内存,而外部结构的简单副本不会达到您期望的值。

您需要正确地序列化此结构。

+0

你如何正确序列化一个对象?在我读的一本书中,他们有一个“熊”对象,他们写入/读取二进制文件就像上面一样。 – rcplusplus 2012-02-24 15:51:17

+1

对于POD(Plain-Old-Datatype),这没问题,但是这个结构包括内部具有动态分配组件的元素,一个按位拷贝(这实际上是'write'的作用),并不理解'string'真的将内容中的内容存储在内存中。看看诸如boost序列化之类的东西,你会看到如何实现这个策略。 – Nim 2012-02-24 15:53:33

+0

对于POD,暂时存储只是可以的。使用不同的选项或编译器的不同版本重新编译您的程序,并且您可能无法阅读您之前编写的内容。 – 2012-02-24 16:55:01

1
  1. 不要参考使用下划线,请
  2. 传递对象:Bank(string& name),请
  3. 这是邪恶的:fout.write((char *)&bank, sizeof(bank));
  4. 你可能想要写<<>> ostream的运营商的Bank类。

例如:

friend std::ostream& operator<<(std::ostream &out, const Bank& b); 
friend std::istream& operator>>(std::istream &out, const Bank& b); 
+0

出于好奇,下划线和价值传递有什么不对?为什么'fout.write()'_evil_? – rcplusplus 2012-02-24 15:46:39

+1

前两个不是邪恶:)只是良好的做法。获取银行地址并转换为char字符将不起作用。如果您想获取字符串字节,则编写一个GetBytes方法,该方法返回__name :: c_str(),它是一个常量指针。 – vulkanino 2012-02-24 15:50:46

+0

如果我让'char *'''__name'字段,序列化工作好吗? – rcplusplus 2012-02-24 15:56:28

1

不能读取包含的std :: string(或任何非纯醇”数据)fin.read()的对象 -

该对象是以字节流的形式读写的,但std:string包含一个指向内存的指针,该指针存储在其他位置,并且不会与您的fout.write()一起写入,并且您的fin.read()

这是因为它不是ini用你的fin.read()正确地进行了tialized,你正在得到堆错误;当对象超出范围时,正在调用不正确初始化的std :: string的析构函数,并试图释放它不拥有的内存。

您可能想为您的对象编写自定义I/O方法并逐个保存或加载它。要获得此操作的快捷方式,请使用Boost序列化库。

+0

除了在同一个可执行文件中,您甚至无法可靠地使用POD。 – 2012-02-24 16:53:45

0

成员函数写入ostream和读取istream是专门设计用于输入和输出二进制数据。如果你想操作的二进制数据,使用以下命令:

ifstream fin(filename.c_str(), ios::in|ios::binary|ios::ate); 
size = fin.tellg(); 
memblock = new char [size]; 
fin.seekg(0, ios::beg); 

if (fin.good()){ 
    fin.read(memblock, size); 
    fin.close(); 
} 
delete[] memblock; 
+0

这是不正确的。成员函数'write'和'read'用于编写预先格式化的文本,并读取要在iostream之外解析的原始文本。标准中不支持任何二进制格式(可能因为没有广泛使用的二进制格式)。 – 2012-02-24 16:57:36