2

I have tried using fstream and although apparently compiled, the program crash. Please tell me what is wrong in above code and what should I do to correct it

I want to recover all elements of miperro5[] from the binary file into a new array miperro6[]. As might fix it?

#include <stdio.h>
#include <iostream>
#include <Perro.hpp>
#include <fstream>

using std::cout;
using std::endl;

int main(int argc, char **argv)
{
    Perro miperro5[5];
    Perro miperro6[5];
    miperro5[2].nombre = "lucky";

    std::ofstream myfile;
    myfile.open("example.bin", std::ios::out | std::ios::app | std::ios::binary);
    for (int i = 0; i < 5; i++){
        myfile.write(reinterpret_cast<char *>(&miperro5[i]), sizeof(Perro));
    }
    myfile.close();

    std::ifstream myfile2;
    myfile2.open("example.bin", std::ios::in | std::ios::binary);
    for (int j = 0; j < 5; j++){
        myfile2.read(reinterpret_cast<char *>(&miperro6[j]), sizeof(Perro));
    }
    myfile2.close();

    for (int z = 0; z < 5; z++){
        cout << miperro6[z].nombre << endl;
    }

    std::cin.get();
    return 0;
}

This is Perro.hpp

#ifndef PERRO_HPP
#define PERRO_HPP
#include <string>
class Perro
{
public:
    Perro();
    ~Perro();
    void ladrar();
    void comer();
    std::string nombre;
    int edad;
    int arra[2];


};

#endif // PERRO_HPP
Jseh
  • 21
  • 1
  • 2
  • This trick of `reinterprete_cast` only work with object that have always the same size, in the case of `Perro` one never known the real size of `std::string nombre`, and even this member is initialized empty. You should create a method in `Perro` that save and load the object from a stream, and in this case you could read `std::string` and `reinterprete_cast` the other 2 members. – NetVipeC Aug 02 '14 at 04:49

3 Answers3

0

The problem is probably here:

myfile.write(reinterpret_cast<char *>(&miperro5[i]), sizeof(Perro));

You are writing a struct Perro to file, but you have to make sure that you write it correctly, since in general the something like

struct Foo
{
    std::string str;
};

will not be written on the disk as you would think. Only structs containing ONLY plain old data (POD) can be written as you are doing, for the rest you have to manually write each field. Look for boost::serialization if you want to simplify your life with object serialization.

vsoftco
  • 55,410
  • 12
  • 139
  • 252
0

You can use fwrite and fread the way you are using only when a class is a POD type.

Perro is not a POD type since it has a member variable of type std::string.

Let me use a simpler example to illustrate the problem. Let's say you have:

#include <iostream>
#include <fstream>

struct A
{
   A(int s) : ip(new int[s]), size(s) {}
   ~A() { delete [] ip; }
   int* ip;
   int size;
};

int main()
{
   A a1(10);
   A a2(5);

   std::cout << "a1.size: " << a1.size << ", a1.ip: " << a1.ip << std::endl;

   std::ofstream myfile;
   myfile.open("example.bin", std::ios::out | std::ios::binary);
   myfile.write(reinterpret_cast<char *>(&a1), sizeof(A));
   myfile.close();

   std::ifstream myfile2;
   myfile2.open("example.bin", std::ios::in | std::ios::binary);
   myfile2.read(reinterpret_cast<char *>(&a2), sizeof(A));
   myfile2.close();

   std::cout << "a2.size: " << a2.size << ", a2.ip: " << a2.ip << std::endl;

   return 0;
}

When I run the program, I get the following output:

a1.size: 10, a1.ip: 0x16b4010
a2.size: 10, a2.ip: 0x16b4010
*** Error in `./test-328': double free or corruption (fasttop): 0x00000000016b4010 ***
Aborted

What happened here is that binary value of a1.ip was written out to the file and the same binary value was read into a2.ip. After the read, both a1.ip and a2.ip point to the same memory location. These causes two problems:

  1. The destructor ends of calling delete on the same address twice.
  2. The initial memory allocated for a2 never gets deallocated.

Similar things happen when one of your member variables is a std::string.

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

main.cpp

#include <stdio.h>
#include <iostream>
#include <fstream>
#include "Perro.hpp"

using std::cout;
using std::endl;

int main(int argc, char **argv)
{
    Perro miperro5[5];
    Perro miperro6[5];
    miperro5[2].nombre = "lucky";

    std::ofstream myfile;
    myfile.open("example.bin", std::ios::out | std::ios::app | std::ios::binary);
    for (int i = 0; i < 5; i++){
        myfile << miperro5[i];
    }
    myfile.close();

    std::ifstream myfile2;
    myfile2.open("example.bin", std::ios::in | std::ios::binary);
    for (int j = 0; j < 5; j++){
        myfile2 >> miperro6[j];
    }
    myfile2.close();

    for (int z = 0; z < 5; z++){
        cout << miperro6[z].nombre << endl;
    }

    std::cin.get();
    return 0;
}

Perro.hpp

#ifndef PERRO_HPP
#define PERRO_HPP
#include <string>
#include <ostream>
#include <istream>
class Perro
{
public:
    Perro() {};
    ~Perro() {};
    void ladrar();
    void comer();
    std::string nombre;
    int edad;
    int arra[2];
};
std::ostream & operator<<(std::ostream & ostrm, const Perro & src)
{
    // nombre
    std::string::size_type cch = src.nombre.length();
    ostrm // std::string is not a primitive type, so we need to do a (sort of) deep copy.
        .write(reinterpret_cast<const char *>(&cch), sizeof(std::string::size_type))
        << src.nombre.c_str() // or use unformatted output `write`
    ;
    // edad, arra[2]
    ostrm
        .write(reinterpret_cast<const char *>(&src.edad), sizeof(int)) // edad
        .write(reinterpret_cast<const char *>(&src.arra), sizeof(int) * 2) // arra[2]
    ;
    return ostrm;
}
std::istream & operator>>(std::istream & istrm, Perro & dst)
{
    // Read the string back.
    std::string::size_type cchBuf;
    istrm.read(reinterpret_cast<char *>(&cchBuf), sizeof(std::string::size_type));
    char * buf = new char[cchBuf];
    istrm.read(buf, cchBuf);
    dst.nombre.assign(buf, cchBuf);
    delete [] buf;
    // Read other data members of primitive data type.
    istrm
        .read(reinterpret_cast<char *>(&dst.edad), sizeof(int)) // edad
        .read(reinterpret_cast<char *>(&dst.arra), sizeof(int) * 2) // arra[2]
    ;
    // Return the stream so it can be examined on its status.
    return istrm;
}

#endif // PERRO_HPP
Cody
  • 609
  • 4
  • 21