Reputation: 430
I want to save a vector of objects into file. What is the most efficient way to do it? Should I load the whole vector when the program starts, operate on it locally and then save it as the program exits or access the file every time I need to change something inside vector?
Also, is it even possible to save the whole vector at once or I need to save elements one by one?
Upvotes: 0
Views: 2167
Reputation: 5566
If your data is POD (and thus each element is the same size), you can use the following ideas. If not, the following ideas might betoo difficult to use easily.
The following code shows that you 'can' use "ostream::write" to put the binary data out to a file. vector<> promises that the objects are front-to-back (or back-to-front, as you wish) in memory, so they are lined up and relatively compact for transfer.
Reading back in to memory is similar, using "istream::read". But here you need to allocate the buffering 1st.
In the following code, I allocated 6 objects for write, then found the beginning of the vector memory and wrote all 6 objects in with one 'write'.
For the read, I instantiated and loaded 6 objects into a new vector (note that vector provides a slicker way to allocate and instantiate the objects using the default ctor).
How you pass the number of objects from the write effort to the read? This can be computed from the file size. (see stat)
Using the reinterpret-cast is not (generally) acceptable, and may not be portable. But sometimes you just gotta do it.
Hopefully this 'evil' will prompt a number of better choices.
I left in some debug std::cout's ... hope you find them helpful.
edit - 4/8 -- cleaned the code somewhat
// create a vector of 6 clear elements to save to file
std::vector<Something> vec6W(6); // default ctor clears contents
// change several elements by init'ing them
vec6W[0].init();
// [1] is clear (all 0's)
vec6W[2].init();
vec6W[3].init();
// [4] is clear (all 0's)
vec6W[5].init();
std::cout << " vec6W.size(): " << vec6W.size() << std::endl; // 6
std::cout << " sizeof(vec6W): " << sizeof(vec6W) << std::endl; // 12
std::cout << " sizeof(something): " << sizeof(Something) << std::endl; // 100
std::cout << " sizeof(vec6W[3]): " << sizeof(vec6W[3]) << std::endl; // 100
// #elements bytes per element
std::streamsize nBytes = (vec6W.size() * sizeof(Something));
std::cout << " nBytes : " << nBytes << std::endl; // 600
// simulate a file using
std::stringstream ss;
// note size
std::cout << " ss.str().size(): " << ss.str().size() << std::endl; // 0
// identify address of 1st element of vector
// cast to a 'const char*' for 'write()' method
const char* wBuff = reinterpret_cast<const char*>(&vec6W[0]);
// report address
std::cout << " static_cast<const void*>(wBuff): " << std::hex
<< static_cast<const void*>(wBuff) << std::dec << std::endl;
//write vector to ss
ss.write(wBuff, nBytes);
std::cout << "\n note ss content size change " << std::endl;
std::cout << " ss.str().size() : " << ss.str().size() << std::endl;
// //////////////////////////////////////////////////////////////////
// build a vector of 6 clear elements to create buffer for read from file
std::vector<Something> vec6R(6);
// identify address of 1st element of vector
std::cout << " &vec6R[0] : " << std::hex << (&vec6R[0]) << std::dec << std::endl;
char* rBuff = reinterpret_cast<char*>(&vec6R[0]);
// read vector from ss
(void)ss.read(rBuff, nBytes); // read back same number of bytes
// //////////////////////////////////////////////////////////////////
// confirm vec6R matches what was written vec6W
int diff = memcmp(wBuff, rBuff, nBytes);
std::cout << (diff ? "FAILED" : "wBuff MATCHES rBuff: SUCCESS") << std::endl;
// now consider comparing vec6R to vec6W element by element
There may be additional issues when the class has virtual methods.
Good luck.
edit -----
Pointers can be handled, but create additional work, and some asymmetry.
Related work might be called "persistant storage".
Also, there exists tools to simplify the additional steps of non-POD data (sorry, I have forgotten the name.)
Upvotes: 1
Reputation: 36597
There is no single answer to this question.
An appropriate approach depends on the needs of your application, why it is saving the file, and what will be done with the file. For example, a file that is intended to opened in another program and understood by a human may be written very differently from a file that just saves program state (i.e. that only a software program needs to make sense of it).
The most efficient depends on your measure of efficiency. Some possible measures include speed of writing, speed of reading, file size, size of code to do the writing, etc etc. Not all of these things go together - for example, an archiver program may choose a slow approach to writing a file, in order to achieve fast read speeds.
Usually, writing a collection of objects involves writing all the objects individually, plus some additional book-keeping (e.g. output the number of objects first), particularly if the file needs to be read later. However, smarter algorithms might derive some sort of summary data from a set of objects. For example, assume a vector containing the integers 1 to 20 in order. One way of writing is to write all 20 values. Another is simply to emit the string "1-20".
Upvotes: 1