gpt4 book ai didi

c++ - 构造一个附加了可变数据量的对象

转载 作者:行者123 更新时间:2023-11-28 08:24:28 24 4
gpt4 key购买 nike

我正在开发一个消息传递运行时系统,该系统具有现有的消息分配代码,当消息包含可变长度数据时看起来像这样:

struct MsgBase {
void* operator new(size_t obj_size, int arr1_size, int arr2_size);
};
struct Msg : MsgBase {
double *arr1;
double *arr2;
};
struct MsgBase {
void* operator new(size_t obj_size, int arr1_size, int arr2_size) {
size_t offsets[2];
offsets[0] = obj_size;
offsets[1] = obj_size + sizeof(double)*arr1_size;
Msg *m = (Msg *)malloc(offsets[1] + sizeof(double)*arr2_size);
m->arr1 = (char*)m + sizeof(double)*arr1_size;
m->arr1 = (char*)m->arr1 + sizeof(double)*arr2_size;
return m;
}
};

出于硬件接口(interface)的原因,消息必须分配为一个大缓冲区,事后复制到这样的缓冲区会降低性能1

和(很多)客户端代码

Msg *msg = new (12, 17) Msg;
msg->arr1[6] = 543.43;

我们刚刚遇到的问题是 g++ 4.4(与早期版本不同)在指针返回到之前将 sizeof(Msg) 字节清零我们,因此不会保留那些指向缓冲区的偏移指针。因此,第二行代码会导致段错误。

如果我们声明构造函数Msg::Msg(),则不会发生清零,但我的直觉是编译器有权在调用构造函数之前将分配清零.

  • 那么,我的直觉是否正确,即使我们显式声明构造函数,像这样的代码也无法真正保证工作?

  • 假设上面的代码不能继续工作,我还有希望保留客户端界面吗?这个的替代品看起来如何?


注意: MsgBase 类和伴随的 operator new() 是从客户端提供的接口(interface)定义生成的,该接口(interface)定义类似于

message Msg {
double arr1[];
double arr2[];
};

而客户端代码负责定义Msg 本身并确保它继承自MsgBase。因此,我们可以更改关于 MsgBase 的任何内容,但几乎不能更改关于 Msg 的任何内容,而无需强制对现有应用程序代码进行更改。

  1. 不要告诉我去分析那个。我们对此进行了大量基准测试(我们在 top500 的前 10 名中的几个上运行),并尽可能地尝试零复制。我们目前不在这里制作任何拷贝,回归会很糟糕。

最佳答案

您是对的,operator new 写入内存的任何内容都不能保证得到保留。所以不,您不能保留当前损坏的调用语法。

使用工厂函数和库提供的 placement new。像这样:

struct Msg
{
double* const arr1;
double* const arr2;
private:
Msg(double* p1, double* p2) : arr1(p1), arr2(p2) {}
Msg(const Msg&); // deleted copy-constructor
// having const members prevents assignment operator from being implicitly generated
public:
static Msg* Create( size_t arr1_len, size_t arr2_len )
{
void* raw = ::operator new(sizeof (Msg) + (1 + arr1_len + arr2_len) * sizeof (double));
// note ugly math to properly align double, assumes sizeof (double) is a power of 2
// consider using alignof (double) instead of sizeof (double) if your compiler supports it
double* p = reinterpret_cast<double*>((reinterpret_cast<intptr_t>(raw) + sizeof (Msg) + sizeof (double)) & ~(sizeof (double) - 1));
return new (raw) Msg(p, p + arr1_len);
}
};

注意:您可以通过使用线程局部变量和构造函数来保留当前语法...基本上您的自定义 operator new 会将指针或大小放入 TLS,然后构造函数将读取 TLS 并适本地设置指针。我想我会传递大小,因为编译器可能会向 operator new 请求一些额外的内存并在实际对象前面填充调试信息。

关于c++ - 构造一个附加了可变数据量的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4524287/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com