我有一些已有15年历史的C ++代码,我试图将其带到更现代的时代。在这个阶段,我试图获得使用Visual C ++ 6.0编译的代码,现在使用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));
当我阅读评论时,我会担心...我们真的可以假设一个向量在连续的内存中吗?如果没有,有什么更好的方法呢?我是否需要遍历矢量内容&amp;从myClassType写出每组数据?
实际上,编译器抱怨将begin()的结果转换为char *。
答案 0 :(得分:4)
std::vector
保证将其元素存储在连续的内存中,因此,如果myClassType
为POD type,您尝试执行的操作就可以了。否则,您必须循环遍历std::vector
,根据需要将每个元素一次一个地序列化为平面格式。
但是,std::vector::begin()
方法将迭代器返回给元素数据的第一个元素,而不是指针。迭代器是类指针,但是不是指针。容器实现被允许使用实际指针作为迭代器,但更多次使用包装类。不要依赖这种行为!按照预期的方式使用迭代器API,并且不要假设迭代器是实际的指针。
要获得指向矢量第一个元素数据的有效指针,您需要:
使用std::vector::data()
(仅限C ++ 11及更高版本):
myVect.data()
即使向量为空,这也是安全的。当data()
为0时,nullptr
可能会或可能不会返回size()
,但这是正常的,因为write()
在size()
为0时不会尝试访问任何内存。
bool OK = File.write((char*) myVect.data(), myVect.size() * sizeof(myClassType));
使用std::vector::operator[]
获取对第一个元素的引用,然后使用operator&
获取元素的地址:
&myVect[0]
请注意operator[]
不执行边界检查,因此如果指定的索引是&gt; = size()
,将返回未定义的指针,因此在写入之前一定要检查向量是否为空:
bool OK = myVect.empty() || File.write((char*) &myVect[0], myVect.size() * sizeof(myClassType));
与#2相同,但改为使用std::vector::front()
:
&myVect.front()
请注意,在空向量上调用front()
是未定义的行为,因此请务必检查向量是否为空:
bool OK = myVect.empty() || File.write((char*) &myVect.front(), myVect.size() * sizeof(myClassType));
使用begin()
获取第一个元素的迭代器,然后使用operator*
取消引用迭代器以获取对其指向的元素的引用,然后可以获取元素的地址:
&(*(myVect.begin()))
注意begin()
在向量为空时返回end()
迭代器,并且取消引用end()
迭代器是未定义的行为,因此请务必检查向量是否为空:
bool OK = myVect.empty() || File.write((char*) &(*(myVect.begin())), myVect.size() * sizeof(myClassType));
答案 1 :(得分:4)
Remy Lebeau的回答是正确的。要添加它,如果您想为代码添加更多安全性,还应使用static_assert
确保myClassType
可以安全地编写,如下所示:
#include <type_traits>
static_assert(std::is_pod_v<myClassType>, "myClassType is potentialy unsafe to write as-is");
如果有人在将来以myClassType
代码中使用不安全的方式修改File.write()
,则此编译时检查将捕获它。