2012-02-24 13 views
1

を書き、私は非常に小さなコードスニペットを書いて、すでに次のエラーを得ている:SIGABRT/

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

問題は、私は、コンパイラのが話して何ポインタ見当がつかない、です。私は読み書き機能にアドレスで変数を渡しますが、私が知る限りそれを解放することはありませんでした。私のコードのどこにエラーがありますか?私はLeaksとZombiesでそれを走らせましたが、何も得られませんでした。ここで

は私のプログラムです:

#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

答えて

1

あなたの銀行のクラスは、STDが含まれているため::文字列、あなたはそれはあなたが考えているようなバイナリとして読み出し/書き込みができません。 std :: stringには内部ポインタがあります。バイナリとして書くと、実際の文字列の内容ではなく、ポインタを書くことになります。同様に、文字列を読むと、ポインタを読み取ることになります。この場合、bankとbank2の両方のオブジェクトに同じメモリを指し示す文字列があるので、そのメモリが解放されると2回解放されます。

銀行データをファイルに書き込む別の方法が必要です。この場合、銀行名の付いた単純なASCIIファイルが問題ありません。

+0

しかし、後で定義する 'Account'型のオブジェクトを格納する'ベクトル 'のような複数のフィールドを持つことになります。バイナリファイルは簡単に動作するように見えるので、データ全体を読み込み、解析することはできません。 – rcplusplus

+0

残念ながら、それを簡単に行うことができるC++には何も組み込まれていません。 boost :: serializationのようなライブラリは簡単にしようとしますが、クラスを変更してもシリアライゼーションコードを変更する必要があります。 –

1

std::stringはそのようにコピーできないため、やっていることはできません。内部的にはstringオブジェクトはメモリを割り当て、外側の構造の単純なコピーは期待したことをしません。

この構造を適切にシリアル化する必要があります。

+0

どのようにオブジェクトを正しくシリアル化しますか?私が読んだ本では、彼らは上記のようにバイナリファイルに書き込んだり読んだりする 'Bear'オブジェクトを持っていました。 – rcplusplus

+1

POD(Plain-Old-Datatype)の場合、これは大丈夫ですが、この構造体には内部的に動的に割り当てられた要素、ビット単位のコピー(効果的には 'write'がします)、' string'メモリ内の他の場所にコンテンツを格納します。 boost serializationのようなものを見ると、この戦略を実装する方法がわかります。 – Nim

+0

PODの場合、一時的な保管に限ります。異なるオプション、または異なるバージョンのコンパイラでプログラムを再コンパイルすると、以前に書いたものを読むことができない場合があります。 –

1
  1. は、参照することにより、
  2. パスオブジェクトをアンダースコアをしてください使用しないでください:Bank(string& name)、これが悪である
  3. してください:fout.write((char *)&bank, sizeof(bank));
  4. あなたのBankクラスの<<>>のostream演算子を書きたいことがあります。たとえば、

:あなたはfin.read()と(プレーン老いたデータではありませんか何か)のstd ::文字列を含むオブジェクトを読み取ることができません

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

+1

最初の2つは悪ではありません:)ただ良い練習です。 Bankのアドレスを取得してcharにキャストすることはできません。文字列バイトを取得する場合は、constポインタである__name :: c_str()を返すGetBytesメソッドを記述します。 – vulkanino

+0

'char *'型の '__name'フィールドを作ると、シリアル化はうまくいくのですか? – rcplusplus

1

-

オブジェクトはバイトのストリームとして読み書きされますが、std:stringには他の場所に格納され、fout.write()で書き込まれず、fin.read()で正しく初期化されないメモリへのポインタが含まれます。

これはiniではないためですあなたのヒープエラーを取得しているあなたのfin.read()で適切に調整してください。オブジェクトが範囲外になると、不適切に初期化されたstd :: stringのデストラクタが呼び出され、所有していないメモリを解放しようとしています。

おそらく、あなたのオブジェクトのカスタムI/Oメソッドを記述し、オブジェクトを個別に保存またはロードしたいとします。これを行うためのショートカットは、Boostシリアル化ライブラリを使用してください。

+0

同じ実行可能ファイル内を除いて、PODでこれを確実に行うことはできません。 –

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の外で解析される生のテキストを読み込むように設計されています。標準のバイナリ形式はサポートされていません(多分広く使われているバイナリ形式がないためです)。 –