用更好的替代品替换 reinterpret_cast?
Replacing reinterpret_cast with better alternatives?
我在我的项目中有几个地方使用 reinterpret_cast
到 read/write 来自流的无符号整数。考虑以下函数:
size_t ReadSize(std::stringstream& stream) {
char buf[sizeof(size_t)];
stream.read(buf, sizeof(size_t));
return *(reinterpret_cast<size_t*>(buf));
}
void WriteSize(std::stringstream& stream, size_t n) {
stream.write(reinterpret_cast<char*>(&n), sizeof(size_t));
}
我开始对使用 reinterpret_cast 感到有点不自在,尽管我用它没有任何问题,所以我想知道,是否有更好的替代方案?假设我在流中只有 4 个字节应该代表这个整数。
我认为 static_cast
在这里也不适用。有什么建议吗?
P.S。我目前不关心使用 reinterpet_cast
可能引起的可移植性或其他特定于平台的问题。我正在为 Windows 机器写这篇文章。
虽然 read
(和 write
)函数被指定为采用 char*
,但您实际上不必传递字符数组,只需将指针转换为指向read
(或write
)调用中的实际变量改为:
std::size_t size;
if (stream.read(reinterpret_cast<char*>(&size), sizeof(size_t)))
return size;
return 0; // Or something else on error
在一个不相关的说明中,我建议您将流参数更改为 std::istream
引用,然后您可以将该函数用于任何输入流。
所以你的代码的问题是,如果一个小端系统写入数据,而一个大端系统读取它。
在这里,reinterpret_cast<>
将获取位图并应用它,而不管任何数据不兼容。
优先顺序是:-
const_cast
仅用于remove/add const.
dynamic_cast
将预先创建的对象转换为兼容 base/derived.
static_cast
使用编译时信息执行与 dynamic_cast
相同形式的转换
reinterpret_cast
将内存视为源和目标的联合。
- C cast
(void*)f;
使用 reinterpret_cast
/ static_cast
. 之一转换类型
所以避免C cast
。这是因为您无法真正判断编译器会选择什么。
const_cast
/ dynamic_cast
不解决你的问题。
所以最好的选择是reinterpret_cast
。
因为你使用了stringstream,你可以直接访问它用作缓冲区的字符串:
ReadSize(std::stringstream& stream) {
return *(reinterpret_cast<size_t*>(stream.str().c_str()));
}
这样可以节省一些复制工作。
无论如何,那不是你的问题。只有当您的流提供的数据与您的机器使用的字节序相同时,您的代码才会按预期工作。您可能更愿意明确处理字节序:
ReadSize(std::istream& stream) {
char buf[sizeof(size_t)];
stream.read(buf, sizeof(size_t));
return (static_case<size_t>(buf[0]) << 24) |
(static_case<size_t>(buf[1]) << 16) |
(static_case<size_t>(buf[2]) << 9) |
(static_case<size_t>(buf[3]));
}
所以顺便说一下,你也摆脱了 reinterpret_cast<>
。
您的代码假设了 size_t
的大小,即使在 Windows 上也不总是 4 个字节。如果将 4 个字节写入流,并且您尝试使用 sizeof(size_t)
为 8 的编译代码读取它,会发生什么情况?
您可以使用以下函数安全且可移植地(也可以)将字节转换为整数。当然,它假定提供的数组足够大。
template<class T>
T ComposeBytes(const char *bytes)
{
T x = 0;
for (size_t i = 0; i < sizeof(T); i++)
{
x |= (static_cast<unsigned char>(bytes[i]) << (i * CHAR_BIT));
}
return x;
}
编辑:修复了 char
被签名的问题。
我在我的项目中有几个地方使用 reinterpret_cast
到 read/write 来自流的无符号整数。考虑以下函数:
size_t ReadSize(std::stringstream& stream) {
char buf[sizeof(size_t)];
stream.read(buf, sizeof(size_t));
return *(reinterpret_cast<size_t*>(buf));
}
void WriteSize(std::stringstream& stream, size_t n) {
stream.write(reinterpret_cast<char*>(&n), sizeof(size_t));
}
我开始对使用 reinterpret_cast 感到有点不自在,尽管我用它没有任何问题,所以我想知道,是否有更好的替代方案?假设我在流中只有 4 个字节应该代表这个整数。
我认为static_cast
在这里也不适用。有什么建议吗?
P.S。我目前不关心使用 reinterpet_cast
可能引起的可移植性或其他特定于平台的问题。我正在为 Windows 机器写这篇文章。
虽然 read
(和 write
)函数被指定为采用 char*
,但您实际上不必传递字符数组,只需将指针转换为指向read
(或write
)调用中的实际变量改为:
std::size_t size;
if (stream.read(reinterpret_cast<char*>(&size), sizeof(size_t)))
return size;
return 0; // Or something else on error
在一个不相关的说明中,我建议您将流参数更改为 std::istream
引用,然后您可以将该函数用于任何输入流。
所以你的代码的问题是,如果一个小端系统写入数据,而一个大端系统读取它。
在这里,reinterpret_cast<>
将获取位图并应用它,而不管任何数据不兼容。
优先顺序是:-
const_cast
仅用于remove/add const.dynamic_cast
将预先创建的对象转换为兼容 base/derived.static_cast
使用编译时信息执行与dynamic_cast
相同形式的转换
reinterpret_cast
将内存视为源和目标的联合。- C cast
(void*)f;
使用reinterpret_cast
/static_cast
. 之一转换类型
所以避免C cast
。这是因为您无法真正判断编译器会选择什么。
const_cast
/ dynamic_cast
不解决你的问题。
所以最好的选择是reinterpret_cast
。
因为你使用了stringstream,你可以直接访问它用作缓冲区的字符串:
ReadSize(std::stringstream& stream) {
return *(reinterpret_cast<size_t*>(stream.str().c_str()));
}
这样可以节省一些复制工作。
无论如何,那不是你的问题。只有当您的流提供的数据与您的机器使用的字节序相同时,您的代码才会按预期工作。您可能更愿意明确处理字节序:
ReadSize(std::istream& stream) {
char buf[sizeof(size_t)];
stream.read(buf, sizeof(size_t));
return (static_case<size_t>(buf[0]) << 24) |
(static_case<size_t>(buf[1]) << 16) |
(static_case<size_t>(buf[2]) << 9) |
(static_case<size_t>(buf[3]));
}
所以顺便说一下,你也摆脱了 reinterpret_cast<>
。
您的代码假设了 size_t
的大小,即使在 Windows 上也不总是 4 个字节。如果将 4 个字节写入流,并且您尝试使用 sizeof(size_t)
为 8 的编译代码读取它,会发生什么情况?
您可以使用以下函数安全且可移植地(也可以)将字节转换为整数。当然,它假定提供的数组足够大。
template<class T>
T ComposeBytes(const char *bytes)
{
T x = 0;
for (size_t i = 0; i < sizeof(T); i++)
{
x |= (static_cast<unsigned char>(bytes[i]) << (i * CHAR_BIT));
}
return x;
}
编辑:修复了 char
被签名的问题。