-1

J'optimise actuellement une application gourmande en mémoire pour occuper moins de mémoire. Ce que j'essaie de faire dans le code suivant est d'allouer dynamiquement les objets du flux de fichiers ifstream et ofstream afin de les libérer exactement après que son utilisation ne soit plus nécessaire. Le code fonctionne parfaitement pour l'allocation/désaffectation de ofstream mais tombe en panne à l'exécution en raison d'un éventuel défaut de segmentation lorsque le contenu de la mémoire de ifstream est désaffecté. Ce qui suit est un extrait du code d'origine:Erreur d'exécution inattendue (erreur de segmentation)

#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"; 

} 

Le code ci-dessus enregistre un objet à un fichier binaire lorsque l'utilisateur évoque enregistrer des fonctions. Comme indiqué dans le code ci-dessus, la désaffectation du pointeur inputFile génère une erreur d'exécution.

S'il vous plaît noter que j'utilise clang (plus précisément, clang ++) pour compiler le projet.

Merci d'avance.

+1

S'il vous plaît [modifier] votre question pour fournir un [mcve]. –

+3

Vous venez d'un arrière-plan Java ou C#? Parce qu'en C++ vous n'avez pas besoin d'utiliser 'new' pour créer une instance d'objets. Et cela conduit à moins de pointeurs, et moins de pointeurs sont une bonne chose parce que l'utilisation de pointeurs à tort est la raison la plus fréquente de plantages. –

+2

'outputFile = new ofstream (" bFile.bin ", ios :: binary);' est une très mauvaise idée ... Essayez-vous délibérément de vaincre [RAII] (https://en.wikipedia.org/wiki/ Resource_acquisition_is_initialization)? – WhiZTiM

Répondre

2

std::basic_istream::read(char_type*address, streamsize count) lit jusqu'à count caractères et les place à address. Il ne pas créer des objets à address, comme vous semblez le croire. Le address doit pointer vers un bloc de mémoire valide d'au moins count*sizeof(char_type). En passant l'adresse d'un objet delete d, vous ne respectez pas cette condition: votre code est cassé et erreur de segmentation est pas inattendue.

EDIT

Ce que vous faites est undefined behaviour, UB court. Lorsque vous faites cela, rien n'est garanti et aucune logique sur ce qui se passe dans quel ordre invalide. Le programme est autorisé à faire n'importe quoi, y compris se briser immédiatement, en cours d'exécution pendant un certain temps, puis se briser, ou "make demons fly out of your nose".

Dans votre cas, je suspecte que std::basic_istream::read() écrit dans une mémoire non protégée provoque l'écrasement de certaines données, par exemple l'adresse d'un autre objet, ce qui provoque ultérieurement l'erreur de segmentation. Mais c'est de la spéculation pure et pas vraiment digne d'être poursuivie.

Dans votre cas, aucun objet est créé. Le fichier binaire contient juste quelques octets. Le read() les copie à l'adresse fournie, qui n'était pas réservée à cet effet. Pour éviter l'UB, ajoutez simplement

obj = new dummy; 

avant la read pour créer un objet.

Si vous souhaitez réutiliser la mémoire de l'objet précédent, vous pouvez utiliser placement new (voir les points 9 et 10 de ce lien). Par exemple

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 

Notez que si la lecture ne génère pas un objet valide, l'utilisation ultérieure de cet objet, à savoir l'appel à son destructor, peut se bloquer.

Cependant, tout ce micro-optimisation est inutile de toute façon (il ne vaut que faire si vous pouvez éviter de nombreux appels à new et delete). Ne perds pas ton temps avec ça.

+0

Je ne comprends toujours pas. Si le problème consistait à "passer l'adresse d'un objet supprimé", la valeur stockée ne s'afficherait pas car il y aurait une erreur lors de l'appel de read(). Et l'objet n'est-il pas déjà créé lorsque vous le lisez à partir d'un fichier binaire? – hecate

+0

@hecate bienvenue à un comportement indéfini !! [version alternative] (http://coliru.stacked-crooked.com/a/788ceb42e90b4904). Vous pouvez utiliser une portée pour contrôler quand l'objet est désalloué, recherchez RAII. –