考虑一个班级' B'其中包含按特定顺序排列的简单char
成员变量。
class B {
char x1;
char x2;
char x3;
char x4;
}
我有一个缓冲区A
已经包含与B
中定义的顺序相同的数据。另一个进程已经加载A
数据。
char A[4];
是否可以构造包含B
数据的A
类型的对象,而不构造函数复制数据?也就是说,我想" overlay"将B
对象放到A
缓冲区上,这样我就可以对数据使用B
方法,而不会产生副本的开销或内存的分配。
假设问题1有一个解决方案,我是否有任何理由不能定义一个派生自D
的类B
,并且有一些方法可以引用B
的成员变量,但它本身不包含新成员变量?
答案 0 :(得分:6)
尽管不愉快,但没有合法(标准)的方法来实现这一目标。相反,你有一个非法的,但通常是工作的(用于过多的地方)或合法的,但优化依赖。
无论方法如何,这都假设B成员没有填充(为gcc添加^{}/[\w-]+\.(?:gif|jpg|jpeg|tiff|png)$
,或者编译器为B定义添加其他内容以确保没有填充)。
首先是非法的 - 您可以为该类型添加别名。这违反了严格的别名规则,但已知可以在许多平台和编译器上运行。代码示例:
[[gnu::packed]]
第二个选项是依赖编译器的优化器。它通常足够强大,可以实现不需要实际复制数据,并且只使用原始值。它不会一直发生,如果您在性能关键部分依赖此技术,则最好检查生成的代码并在每次编译器升级时重新检查它。
此代码假定优化:
const B* b = reinterpret_cast<const B*>(&a[0]);
答案 1 :(得分:6)
由于A
不是B
,因此没有合法的方法将A
视为B
。也就是说,如果A
是一个标准的布局类,那么你应该能够投射它并且它将会工作&#34;工作&#34;但它不合法。例如
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;
}
这是有效的,因为数组和成员在每个类中占用相同的内存部分,但这并不能保证。在x1
B
之后可能存在填充,这意味着并非所有成员都将映射到数组。
现在,如果你重做B
来拥有一个数组而不是像
struct A
{
char data[6] = "hi 0/";
int a = 10;
int b = 20;
};
struct B
{
char data[6];
};
然后您可以使用联盟同时拥有A
和B
,并且由于B
与A
具有相同common initial sequence,因此使用{{} {1}}。这可能看起来像
b
现在演员阵容已经消失,我们有guarantee from the standard这是安全的
答案 2 :(得分:-2)
是的,您可以使用现有缓冲区构建新对象。 您可以简单地使用placement new运算符:
class B
{
char x1;
char x2;
char x3;
char x4;
};
char A[4];
B *b = new(A) B;
现在b使用A [4]的堆栈存储器; 该代码假定B打包时没有任何对齐。
class D : public B
{
public:
void MyMethod();
};
D *d = new (A) D;
您可以在派生类中添加代码,然后使用相同的方法构建D。
以下代码:
#include <new>
#include <iostream>
struct B
{
char x1;
char x2;
char x3;
char x4;
};
char A[4] = { 'M','e','o','w' };
int main()
{
B *b = new (A) B;
std::cout << "The cat's " << b->x1 << b->x2 << b->x3 << b->x4 << std::endl;
return 0;
}
输出:猫的喵
答案 3 :(得分:-2)
是的,你可以;如果您使用此处提出的新展示位置(https://isocpp.org/wiki/faq/dtors#placement-new),它将指定B的内存位置并在此处记录(https://isocpp.org/wiki/faq/dtors#placement-new),
但要注意使用它的析构函数行为,B中的初始化程序会通过覆盖数据来搞砸。您还需要非常小心内存对齐以及对象在这方面的任何要求。
该页面上需要注意的具体警告:
You are also solely responsible for destructing the placed object. This is done by explicitly calling the destructor:
ADVICE: Don’t use this “placement new” syntax unless you have to. Use it only when you really care that an object is placed at a particular location in memory. For example, when your hardware has a memory-mapped I/O timer device, and you want to place a Clock object at that memory location.
和DANGER: You are taking sole responsibility that the pointer you pass to the “placement new” operator points to a region of memory that is big enough and is properly aligned for the object type that you’re creating. Neither the compiler nor the run-time system make any attempt to check whether you did this right. If your Fred class needs to be aligned on a 4 byte boundary but you supplied a location that isn’t properly aligned, you can have a serious disaster on your hands (if you don’t know what “alignment” means, please don’t use the placement new syntax). You have been warned.