Saving and loading objects from classes in C++

I would like to save objects from a parent class in a text file and then be able to load the objects. The problem is that all the objects are pointers so only the addresses of the objects could be saved but not the objects themselves.

For example:

Car {

int hp;
Gear *x; // this object would in my case be a pointer

}

Gear {

double v;
}

So I would like to, for example, save x but x would in my case be a pointer. I have read up on boost libraries and serialization but haven't been able to come up with a solution. I am hoping to find a way to have different levels of saving content. Say, if I want to save everything or just want to save specific parameters. Then loading either everything or just the specific parameters.
Would it be an option, saving the objects together with its objects pointed to?
Like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<iostream>
#include<fstream>

class Gear
{
public:
    friend std::ostream & operator<<( std::ostream &, const Gear &);
    friend std::istream & operator>>( std::istream &, Gear &);
private:
    double v;
};
class Car
{
public:
    friend std::ostream & operator<<( std::ostream &, const Car &);
    friend std::istream & operator>>( std::istream &, Car &);
    ~Car() { delete x; }
private:
    int hp;
    Gear * x;
};

std::ostream & operator<<( std::ostream & os, const Gear & gear)
{
    os << '{' << gear.v << '}';
    return os;
}
std::istream & operator>>( std::istream & is, Gear & gear)
{
    char dump;
    is >> dump >> gear.v >> dump;
    return is;
}

std::ostream & operator<<( std::ostream & os, const Car & car)
{
    os << "{" << car.hp << ',' << *(car.x) << '}';
    return os;
}
std::istream & operator>>( std::istream & is, Car & car)
{
    char dump;
    Gear * gear = new Gear;
    is >> dump >> car.hp >> dump >> *gear >> dump;
    car.x = gear;
    return is;
}

#include<sstream>
#include<vector>

int main()
{
    std::istringstream iss( "{90,{5}}" );
    std::vector<Car> v;
    Car tmp;
    while( iss >> tmp ) v.push_back(tmp);
    for( auto car : v ) std::cout << car << '\n';
}
Boost::Serialization is your answer. You should read up on it more, since it does precisely what you're asking, and handles pointers quite well, even polymorphic pointers.

Here's a quick example I whipped up. A lot of the bloat comes from just filler access code and polymorphic serialization.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <boost/filesystem.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/export.hpp>
#include <fstream>
#include <iostream>


struct Person;
void savePerson(Person& person);
void loadPerson(Person& person);

class Race {
public:
	Race() {}
	Race(std::string name) : raceName(name) { }
	friend class boost::serialization::access;
        template<typename Archive>
	void serialize(Archive& ar, const unsigned version)
	{
		ar & raceName;
	}
	std::string raceName;
};

class Human : public Race {
public:
	Human() : Race("Human") {}
	friend class boost::serialization::access;
        template<typename Archive>
	void serialize(Archive& ar, const unsigned version)
	{
	  ar & boost::serialization::base_object<Race>(*this);
	}
};

class Alien : public Race {
public:
	Alien() : Race("Alien") {}
	friend class boost::serialization::access;
        template<typename Archive>
	void serialize(Archive& ar, const unsigned version)
	{
	  ar & boost::serialization::base_object<Race>(*this);
	}
};

class Person
{
public:
	std::string name;
	Race* race;

	friend class boost::serialization::access;
        template<typename Archive>
	void serialize(Archive& ar, const unsigned version)
	{
		ar & name;
		ar & race;
	}
};

BOOST_CLASS_EXPORT_GUID(Human, "Human");
BOOST_CLASS_EXPORT_GUID(Alien, "Alien");


int main()
{
	Person myPerson;
	myPerson.name = "Test";
	myPerson.race = new Alien();

	savePerson(myPerson);

	Person newPerson;
	loadPerson(newPerson);

	std::cout << newPerson.race->raceName;
}

void savePerson(Person& person)
{
	std::ofstream outputStream;
	outputStream.open("myPerson.txt");
	boost::archive::text_oarchive outputArchive(outputStream);

	outputArchive << person;
	outputStream.close();
}

void loadPerson(Person& person)
{
	std::ifstream inputStream;
	if(boost::filesystem::exists("myPerson.txt"))
	{
		inputStream.open("myPerson.txt", std::ifstream::in);
		boost::archive::text_iarchive inputArchive(inputStream);
		inputArchive >> person;
	}

	if(inputStream.is_open())
		inputStream.close();
}


In this example I save and load a Person from and to and new object. The corresponding serialization file is:

1
2
22 serialization::archive 17 0 0 4 Test 1 1 0
0 5 Alien


As you can see it captures the polymorphic type "Alien" fine, even when it's a pointer. But you'll also notice I had to register the polymorphic types with the archive beforehand, so the library knew what to do with them.

Also note that unless you want to do some "load_construct data" magic, it's easier to just create each of your classes with a default constructor for boost::serialization to utilize. Otherwise, if the class has no default constructor you basically have to "teach" boost serialization how to construct your object, which adds even more bloat.

Let me know if you have any questions.
Last edited on
Thank you both for your ideas! I think the second one is what I need. The saving of the pointers is not needed so I think I wouldn't want to do that but thanks nonetheless nuderobmonkey.

I am going to get back to you Aaron shortly about your example.
Aaron, I sent you a pm.
The saving of the pointers is not needed so I think I wouldn't want to do that but thanks nonetheless nuderobmonkey.

Be sure about this. You can't save anything that contains a pointer directly either, and most containers do. Try it … make a nice vector of 1000 ints, and get the sizeof() it … a binary file write of a vector would lose its data.
Last edited on
Jonnin, thanks for the tip. Aaron's post is essentially what I need. Now it's all about understanding it. Would you say even in his example code that the pointers would be saved?
The problem is that all the objects are pointers so only the addresses of the objects could be saved but not the objects themselves.


It should be no great revelation that the items pointed to by the pointers are the objects themselves, believe it or not.

Presumably the pointers in question here were created by some form of new statement or similar construction.

A pile of object pointers alone are hardly worth saving - not unlike 'a direction without magnitude'. Somewhere along the line whether boost is used or not some form of dereferencing must take place.

Topic archived. No new replies allowed.