-1

私は現在、メモリーを大量に消費するアプリケーションを最適化しています。私が次のコードでやろうとしているのは、ファイルストリームオブジェクトifstreamofstreamを動的に割り当てて、使用がなくなった後に正確に解放することです。このコードはofstreamの割り当て/割り当て解除のために完全に機能しますが、ifstreamのメモリ内容の割り当てが解除されると、セグメンテーション違反のため実行時にクラッシュします。ユーザが機能を保存する連想させる場合、上記のコードは、バイナリファイルにオブジェクトを保存予期しないランタイムエラー(セグメンテーションフォールト)

#include <fstream> 
using namespace std; 

// Dummy class to emulate the issue at hand 
class dummy { 
    private: 
    int randINT; 
    static bool isSeeded; 
    public: 
    dummy() { randINT=rand(); } 
    int getVal() { return randINT; } 
}; 
bool dummy::isSeeded=false; 

int main(int argc, const char* argv[]) { 
    // Binary file I/O starts here 
    dummy * obj; 
    ofstream * outputFile; 
    ifstream * inputFile; 
    outputFile=new ofstream("bFile.bin",ios::binary); 
    if (!(*outputFile).fail()) { 
     obj=new dummy; 
     cout << "Value to be stored: " << (*obj).getVal() << "\n"; 
     (*outputFile).write((char *) obj, sizeof(*obj)); // Save object to file 
     (*outputFile).close(); 
     delete obj; 
     // don't assign NULL to obj; obj MUST retain the address of the previous object it pointed to 
    } else { 
     cout << "Error in opening bFile.bin for writing data.\n"; 
     exit(1); 
    } 
    delete outputFile; // This line throws no errors! 
    inputFile=new ifstream("bFile.bin",ios::binary); 
    if (!(*inputFile).fail()) { 
     (*inputFile).read((char *) obj,sizeof(dummy)); // Read the object of type 'dummy' from the binary file and allocate the object at the address pointed by 'obj' i.e. the address of the previously de-allocated object of type 'dummy' 
     cout << "Stored Value: " << (*obj).getVal() << "\n"; 
     (*inputFile).close(); 
    } else { 
     cout << "Error in opening bFile.bin for reading data.\n"; 
     exit(1); 
    } 
    delete inputFile; // Runtime error is thrown here for no reason! 

    cout << "\n-----END OF PROGRAM-----\n"; 

} 

以下は、元のコードの抜粋です。上のコードで説明したように、inputFileポインタの割り当て解除によって実行時エラーが発生します。

私はclang(より具体的にはclang ++)を使用してプロジェクトをコンパイルしています。

ありがとうございます。

+1

あなたの質問を[編集]して[mcve]を提供してください。 –

+3

あなたはJavaまたはC#の背景から来ていますか? C++では、オブジェクトのインスタンスを作成するために 'new'を使う必要がないからです。ポインタの数が減るとポインタが少なくなります。ポインタを間違って使用するとクラッシュの最も一般的な原因になるためです。 –

+2

'outputFile = new ofstream(" bFile.bin "、ios :: binary);'は非常に悪い考えです...あなたは故意に[RAII](https://en.wikipedia.org/wiki/)を倒そうとしていますか? Resource_acquisition_is_initialization)? – WhiZTiM

答えて

2

std::basic_istream::read(char_type*address, streamsize count)は、countまでの文字を読み取り、addressに配置します。 ではなくは、あなたが信じているように、addressにオブジェクトを作成します。 addressは、少なくともcount*sizeof(char_type)のサイズの有効なメモリチャンクを指している必要があります。 deleteオブジェクトのアドレスを渡すと、その状態に違反します。コードが壊れていて、セグメンテーションフォールトが予期しないものではないです。何をやっている

EDIT

は、短いUB undefined behaviourです。あなたがそうするとき、何も保証されず、何が起こるかについての何らかの論理が無効となります。プログラムはすぐにクラッシュしたり、しばらく走ってからクラッシュしたり、"make demons fly out of your nose"など何かをすることができます。

あなたのケースでは、保護されていないメモリに書き込むと、後でセグメンテーションフォルトが発生する別のオブジェクトのアドレスなど、一部のデータが上書きされることが考えられます。しかしこれは純粋な推測であり、実際に追求する価値はない。

あなたの場合、オブジェクトは作成されません。バイナリファイルにはいくつかのバイトが含まれています。 read()は、その目的のために予約されていない、提供されたアドレスにそれらをコピーします。 UBを避けるには、readの前に、オブジェクトを作成するために

obj = new dummy; 

を追加するだけです。

以前のオブジェクトからメモリを再利用する場合は、placement newを使用できます(そのリンクのポイント9と10を参照)。例えば、読み取りが有効なオブジェクトと、そのオブジェクトの後の使用、そのデストラクタに、すなわちコールを生成しない場合、クラッシュすることが

char*buffer = nullptr;     // pointer to potential memory buffer 
if(writing) { 
    if(!buffer) 
    buffer = new char[sizeof(dummy)]; // reserve memory buffer 
    auto pobj = new(buffer) dummy(args); // create object in buffer 
    write(buffer,sizeof(dummy));   // write bytes of object 
    pobj->~pobj();      // destruct object, but don't free buffer 
} 
if(reading) { 
    if(!buffer) 
    buffer = new char[sizeof(dummy)]; // not required if writing earlier 
    read(buffer,sizeof(dummy));   // read bytes into object 
    auto pobj = reinterpret_case<dummy*>(buffer); // no guarantees here 
    use(pobj);       // do something with the object read 
    pobj->~pobj();      // destruct object 
} 
delete[] buffer;      // free reserved memory 

注意。

はしかし、すべてこのマイクロ最適化は、(あなたがnewdelete多く呼び出しを避けることができれば、それはやってのみ価値が)とにかく無意味です。あなたの時間を無駄にしないでください。

+0

私はまだそれを取得しません。問題が「削除されたオブジェクトのアドレスを渡す」場合、read()が呼び出されたときにエラーが発生するため、格納された値は表示されません。バイナリファイルからオブジェクトを読み込むときにオブジェクトが作成されていませんか? – hecate

+0

@hecate未定義の動作へようこそ!! [代替バージョン](http://coliru.stacked-crooked.com/a/788ceb42e90b4904)。スコープを使用して、オブジェクトの割り当てが解除されたときを制御し、RAIIをルックアップできます。 –

関連する問題