我见过一个类,它是一个像这样定义的类。
class StringChild : public StringBase
{
public:
//some non-virtual functions
static StringChild* CreateMe(int size);
private:
unsigned char iBuf[1];
};
静态工厂函数具有以下实现..
return new(malloc(__builtin_offsetof(StringChild ,iBuf[size]))) StringChild();
据我所知,这个函数使用placement new来扩展这个类。
这是否安全只是因为只有一个成员并且它已在堆上分配?
答案 0 :(得分:6)
这是一个旧的C技巧,用于解决普通C中不可用长度数组的问题。是的,它也可以在C ++中工作,只要你使用合适的分配器结构(比如分配一堆原始内存)所需的大小,然后放置新的对象在那里)。只要你没有在分配的内存的末尾徘徊,它就是安全的,但它确实会让至少混淆一些内存调试器。
使用这种技术时你必须确定的一点是,可变长度数组是对象布局中的最后一个元素,否则你将遍历其他内部变量。
然而,我对工厂函数的实现有点怀疑 - 我假设'size'参数实际上是所需的数组大小?另外,不要忘记你必须使用'free'而不是'delete'来释放上面的内存,即使后者在大多数情况下都可以使用。
除非有一个令人信服的理由说明为什么必须以这种方式管理内存,否则我只需用std :: vector替换数组。
答案 1 :(得分:3)
如果iBuf是结构的最后一个成员,那么POD就可以了。非POD的问题可能是例如。编译器可以自由重新排序公共/私有/受保护成员,虚拟基类最终会在最大派生对象IIUC的末尾等等。
你的结构是非POD(它有一个基类)所以我不推荐它。
另外,如果你创建这样的实例
return new(malloc(__builtin_offsetof(StringChild ,iBuf[size]))) StringChild();
你应该确保malloc获取的内存应该是免费释放的,所以删除这样的实例:
obj->~StringChild();
free(obj);
也许您想使用::operator new()
进行分配
答案 2 :(得分:0)
严格来说,由于StringChild
来自StringBase
,因此不安全。 C ++标准没有指定基类子对象的布局。第10条第3款:
未指定在最派生对象(1.8)中分配基类子对象的顺序。
如果StringChild
是POD结构,那么这种技术是安全的。