C++ 对象(标准定义)是否保留在内存映射文件中?
Do C++ Objects (Standard Definition) persist in memory map files?
受Dealing with large data binary files
启发的问题
Link 到 Object
程序(1)创建一个内存映射文件并向其写入一些对象(C++标准定义),关闭文件并退出。
程序 (2) 将上述文件映射到内存中并尝试通过 reinterpret_cast
.
访问对象
这是否符合标准,因为对象表示没有改变并且对象仍然存在于文件中?
如果这是在 2 个进程之间尝试的,不是使用文件,而是使用共享进程内存,这是否合法?
注意 - 这个问题不是关于存储或共享本地虚拟地址,因为这显然是一件坏事。
不,对象不会以这种方式持久化。
C++ 对象主要由它们的 lifetime 定义,其作用域为程序。
所以如果你想从原始存储中回收一个对象,程序(2)中必须有一个全新的对象,它有自己的生命周期。 reinterpret_cast
'ing 内存不会创建新对象,因此这不起作用。
现在,您可能认为在该内存位置使用普通构造函数就地新建一个对象可以解决问题:
struct MyObj {
int x;
int y;
float z;
};
void foo(char* raw_data) {
// The content of raw_data must be treated as being ignored.
MyObj* obj = new (raw_data) MyObj();
}
但你也不能那样做。允许编译器(有时确实如此)假设这样的构造会破坏内存。有关详细信息以及演示,请参阅 。
如果您想从给定的存储表示中初始化一个对象,您必须使用memcpy()
或等价物:
void foo(char* raw_data) {
MyObj obj;
static_assert(std::is_standard_layout_v<MyObj>);
std::memcpy(&obj, raw_data, sizeof(MyObj));
}
附录: 可以通过在创建对象后用其原始内容恢复内存来完成所需的 reinterpret_cast<>
的等效操作(受 IOC proposal).
#include <type_traits>
#include <cstring>
#include <memory>
template<typename T>
T* start_lifetime_as(void *p)
requires std::is_trivially_copyable_v<T> {
constexpr std::size_t size = sizeof(T);
constexpr std::size_t align = alignof(T);
auto aligned_p = std::assume_aligned<align>(p);
std::aligned_storage_t<size, align> tmp;
std::memcpy(&tmp, aligned_p, size);
T* t_ptr = new (aligned_p) T{};
std::memcpy(t_ptr , &tmp, size);
return std::launder<T>(t_ptr);
}
void foo(char* raw_data) {
MyObj* obj = start_lifetime_as<MyObj>(raw_data);
}
这应该在 C++11 及更高版本中得到明确定义,只要该内存位置仅包含原始数据而没有先前的对象。此外,从粗略的测试来看,编译器似乎在优化方面做得很好。
见 godbolt
受Dealing with large data binary files
启发的问题Link 到 Object
程序(1)创建一个内存映射文件并向其写入一些对象(C++标准定义),关闭文件并退出。
程序 (2) 将上述文件映射到内存中并尝试通过 reinterpret_cast
.
这是否符合标准,因为对象表示没有改变并且对象仍然存在于文件中?
如果这是在 2 个进程之间尝试的,不是使用文件,而是使用共享进程内存,这是否合法?
注意 - 这个问题不是关于存储或共享本地虚拟地址,因为这显然是一件坏事。
不,对象不会以这种方式持久化。
C++ 对象主要由它们的 lifetime 定义,其作用域为程序。
所以如果你想从原始存储中回收一个对象,程序(2)中必须有一个全新的对象,它有自己的生命周期。 reinterpret_cast
'ing 内存不会创建新对象,因此这不起作用。
现在,您可能认为在该内存位置使用普通构造函数就地新建一个对象可以解决问题:
struct MyObj {
int x;
int y;
float z;
};
void foo(char* raw_data) {
// The content of raw_data must be treated as being ignored.
MyObj* obj = new (raw_data) MyObj();
}
但你也不能那样做。允许编译器(有时确实如此)假设这样的构造会破坏内存。有关详细信息以及演示,请参阅
如果您想从给定的存储表示中初始化一个对象,您必须使用memcpy()
或等价物:
void foo(char* raw_data) {
MyObj obj;
static_assert(std::is_standard_layout_v<MyObj>);
std::memcpy(&obj, raw_data, sizeof(MyObj));
}
附录: 可以通过在创建对象后用其原始内容恢复内存来完成所需的 reinterpret_cast<>
的等效操作(受 IOC proposal).
#include <type_traits>
#include <cstring>
#include <memory>
template<typename T>
T* start_lifetime_as(void *p)
requires std::is_trivially_copyable_v<T> {
constexpr std::size_t size = sizeof(T);
constexpr std::size_t align = alignof(T);
auto aligned_p = std::assume_aligned<align>(p);
std::aligned_storage_t<size, align> tmp;
std::memcpy(&tmp, aligned_p, size);
T* t_ptr = new (aligned_p) T{};
std::memcpy(t_ptr , &tmp, size);
return std::launder<T>(t_ptr);
}
void foo(char* raw_data) {
MyObj* obj = start_lifetime_as<MyObj>(raw_data);
}
这应该在 C++11 及更高版本中得到明确定义,只要该内存位置仅包含原始数据而没有先前的对象。此外,从粗略的测试来看,编译器似乎在优化方面做得很好。
见 godbolt