I will propose a third, more with ++ method. What kind of objects you would not have, anyway, they have a common ancestor (yes, void*
is a degenerate case and usually speaks of either a strong premature optimization or simply ignorance of the OOP). So, first - you need to write a base class. Other classes for different data are inherited from it. Even if you need to store a string and a number.
Since there is a base class, you can write std::vector<BaseClassName> vec;
and there is already an array of objects.
Let them need to print all of them. For this, for each successor, you need to define a method
friend ostream& operator<<(ostream& os, const YourType& dt);
And everything, it will be possible to print such code
for (int i = 0; i < vec.size(); i++) std::cout << vec[i] << std::endl;
Very simple.
But let it be necessary to save objects and load from a file. Add a virtual method "getObjectType", "getObjectSize", "saveToStream" and "loadFromStream" to the base type. Each heir will have to determine them. Now when saving somewhere like this
fstream fs; fs.write(vec.size(), sizeof(size_type)); for (int i = 0; i < vec.size(); i++) { int t = vec[i].getObjectType(); size_t s = vec[i].getObjectSize(); fs.write(t, sizeof(t)); fs.write(s, sizeof(s)); vec[i].writeToStream(fs); }
beautiful?
But loading is a bit more complicated. You will need to store an array of object type matches -> constructor reference. But with a small number of objects, you can if-else or switch
size_type c; fs.read(c, sizeof(c)); for (int i = 0; i < c; i++) { int t; size_t s; BaseObj* bb; fs.read(t, sizeof(t)); fs.read(s, sizeof(s)); switch (t) { case 1: bb = new IntObject(); break; case 2: bb = new StringObject(); break; //.... default: // unknown object, can skip fs.seekg (s, fs.beg); break; } bb->loadFromStream(fs, s); }
upd
The main thing to remember is to make the methods virtual, and also to make a virtual destructor. Of course, if classes do not have a common ancestor, then all this will not work.