Reputation: 364
I have some 15-year-old C++ code that I am trying to bring up to more modern times. At this stage, I'm trying to get code that compiled with Visual C++ 6.0 to now compile with VS 2003 (Microsoft Visual C++ .NET 69462-335-0000007-18915).
std::vector<myClassType> myVect;
...
// Assuming vector.begin() simply points to start of a contiguous array
bool OK = File.write((char*) myVect.begin(),
myVect.size() * sizeof(myClassType));
When I read the comment, I get concerned... Can we really assume that a vector is in contiguous memory? If not, what is a better way to do this? Do I need to loop through the vector contents & write each set of data from myClassType?
As it is, the compiler complains about casting the result of begin() to char* anyway.
Upvotes: 0
Views: 2451
Reputation: 595349
std::vector
is guaranteed to store its elements in contiguous memory, so what you are attempting to do is OK, provided that myClassType
is a POD type. Otherwise, you will have to loop through the std::vector
serializing each element one at a time to a flat format as needed.
However, the std::vector::begin()
method returns an iterator to the first element, not a pointer to the element data. An iterator is like a pointer, but is not a pointer. Container implementations are allowed to use actual pointers as iterators, but more times than not they use wrapper classes instead. Do not rely on this behavior! Use the iterator API the way it is intended to be used, and do not assume an iterator is an actual pointer.
To get a valid pointer to the data of the first element of your vector, you need to either:
use std::vector::data()
(C++11 and later only):
myVect.data()
This is safe to use even if the vector is empty. data()
may or may not return nullptr
when size()
is 0, but that is OK since write()
will not try to access any memory when size()
is 0.
bool OK = File.write((char*) myVect.data(), myVect.size() * sizeof(myClassType));
use std::vector::operator[]
to get a reference to the first element, then use operator&
to get the element's address:
&myVect[0]
Note that operator[]
does not perform bounds checking, and thus will return an undefined pointer if the specified index is >= size()
, so make sure to check if the vector is empty before writing it:
bool OK = myVect.empty() || File.write((char*) &myVect[0], myVect.size() * sizeof(myClassType));
same as #2, but using std::vector::front()
instead:
&myVect.front()
Note that calling front()
on an empty vector is undefined behavior, so make sure to check if the vector is empty:
bool OK = myVect.empty() || File.write((char*) &myVect.front(), myVect.size() * sizeof(myClassType));
use begin()
to get an iterator to the first element, then use operator*
to dereference the iterator to get a reference to the element it points to, then you can get the element's address:
&(*(myVect.begin()))
Note that begin()
returns the end()
iterator when the vector is empty, and dereferencing the end()
iterator is undefined behavior, so make sure to check if the vector is empty:
bool OK = myVect.empty() || File.write((char*) &(*(myVect.begin())), myVect.size() * sizeof(myClassType));
Upvotes: 5
Reputation: 29996
Remy Lebeau's answer is correct. To add to it, if you want to add more safety to your code you should also use static_assert
to make sure myClassType
is safe to write like you want:
#include <type_traits>
static_assert(std::is_pod_v<myClassType>, "myClassType is potentialy unsafe to write as-is");
If somebody modifies myClassType
in the future in a way that makes it unsafe to use in your File.write()
code, this compile time check will catch it.
Upvotes: 4