c++对象(标准定义)在内存映射文件中持久化吗?



处理大数据二进制文件启发的问题

指向对象的链接

程序(1)创建一个内存映射文件并向其写入一些对象(c++标准定义),关闭该文件并退出。

程序(2)将上述文件映射到内存中,并尝试通过reinterpret_cast访问对象。

如果对象表示没有改变并且对象仍然存在于文件中,那么这在标准中合法吗?

如果在两个进程之间尝试这样做,不是使用文件,而是使用共享进程内存,这是否合法?

注意-这个问题不是关于存储或共享本地虚拟地址的,因为这显然是一件坏事。

不,对象不会以这种方式持久化。

c++对象主要由它们的生命周期来定义,它的作用域是程序。

所以如果你想从原始存储中回收一个对象,必须在程序(2)中有一个具有自己生命周期的全新对象。reinterpret_cast的内存不会创建一个新对象,所以这不起作用。

现在,您可能认为在该内存位置使用一个简单的构造函数对对象进行就地更新可以达到目的:

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();
}

但是你也不能那样做。编译器被允许(有时确实如此)假设这样的构造会占用内存。请参阅c++在memset之后放置new以获得更多详细信息,以及演示。

如果要从给定的存储表示初始化对象,必须使用memcpy()或等效的:

void foo(char* raw_data) {
MyObj obj;
static_assert(std::is_standard_layout_v<MyObj>);
std::memcpy(&obj, raw_data, sizeof(MyObj));
}

附录:在创建对象之后(受IOC提议的启发),通过用其原始内容重新踩踏内存来实现所需的reinterpret_cast<>是可能的。

#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及以上版本中定义好,只要该内存位置只包含原始数据而不包含先验对象。此外,从粗略的测试来看,编译器似乎在优化这一点上做得很好。

参见on godbolt

相关内容

  • 没有找到相关文章