考虑一个包含char特定顺序的简单成员变量的类'B' .
class B {
char x1;
char x2;
char x3;
char x4;
}
Run Code Online (Sandbox Code Playgroud)
我有一个缓冲区A已经包含与中定义的顺序相同的数据B.另一个进程已经加载A了数据.
char A[4];
Run Code Online (Sandbox Code Playgroud)
是否有可能构造一个B包含数据的类型的对象A,而没有构造函数复制数据?也就是说,我想将一个B对象"覆盖" 到A缓冲区上,这样我就可以B在数据上使用方法,而不会产生副本的开销或内存的分配.
假设有一个问题1的解决方案,是否有任何理由我不能定义一个D派生自的类,B哪个方法引用成员变量B,但它本身不包含新的成员变量?
尽管不愉快,但没有法律(标准)方法来实现这一目标.相反,你有一个非法的,但通常是工作的(用于过多的地方)或合法的,但优化依赖.
无论方法如何,这都假设B成员没有填充(添加[[gnu::packed]]gcc,或者编译器的其他内容为B定义,以确保不会发生填充).
首先是非法的 - 您可以为该类型添加别名.这违反了严格的别名规则,但已知可以在许多平台和编译器上运行.代码示例:
const B* b = reinterpret_cast<const B*>(&a[0]);
Run Code Online (Sandbox Code Playgroud)
第二种选择是依赖编译器的优化器.它通常足够强大,可以实现不需要实际复制数据,并且只使用原始值.它不会一直发生,如果您在性能关键部分依赖此技术,则最好检查生成的代码并在每次编译器升级时重新检查它.
此代码假定优化:
B b;
memcpy(&b, &a[0], sizeof(b));
// use b in non-modifying way
// Compilers usually will not issue a copy here, YMMV
Run Code Online (Sandbox Code Playgroud)
既然一个A不是一个B没有合法的方式来对待A一个B.也就是说,如果A是一个标准的布局类,那么你应该能够投射它并且它将"工作"但它不合法.例如
struct A
{
char data[6] = "hi 0/";
int a = 10;
int b = 20;
};
struct B
{
char x1;
char x2;
char x3;
char x4;
char x5;
char x6;
};
std::ostream& operator <<(std::ostream& os, B& b)
{
return os << b.x1 << b.x2 << b.x3 << b.x4 << b.x5 << b.x6;
}
int main()
{
A a;
B* b = reinterpret_cast<B*>(&a);
std::cout << *b;
}
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为数组和成员在每个类中占用相同的内存部分,但这不能保证.有可能是填充后x1的B这意味着不是所有成员将被映射到阵列.
现在,如果你重新设计B一个数组而不是像单独的成员那样
struct A
{
char data[6] = "hi 0/";
int a = 10;
int b = 20;
};
struct B
{
char data[6];
};
Run Code Online (Sandbox Code Playgroud)
然后你可以使用一个联盟来保持两者A并且B因为它B具有相同的共同初始序列,因为A它是合法使用的b.这可能看起来像
union Converter
{
Converter() : a{} {}
A a;
B b;
};
std::ostream& operator <<(std::ostream& os, B& b)
{
return os << b.data;
}
int main()
{
Converter c;
std::cout << c.b;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
203 次 |
| 最近记录: |