在C中,“没有声明类型的已分配对象”的C ++等价物是什么?
我正在用C ++为我的VM编写一个内存管理器。 好吧,更确切地说,VM指令将被编译成带有嵌入式内存管理器的C ++。 我在处理C方面感觉更舒服,但现在我确实需要exception处理的原生支持,这几乎是我使用C ++的唯一原因。
C和C ++都有严格的别名规则,即两个不兼容类型的对象不应重叠,对于联合,C中有一个小的例外。 但是为了定义内存分配函数的行为,例如malloc
, calloc
, alloca
等,C标准具有以下段落。
6.5-6访问其存储值的对象的有效类型是对象的声明类型(如果有)。 分配的对象没有声明的类型。 如果通过具有非字符类型的类型的左值将值存储到没有声明类型的对象中,则左值的类型将成为该访问的对象的有效类型以及不修改该值的后续访问的有效类型储值。 如果使用
memcpy
或memmove
将值复制到没有声明类型的对象中,或者将其复制为字符类型数组,则该访问的修改对象的有效类型以及不修改该值的后续访问的有效类型是复制值的对象的有效类型(如果有)。 对于没有声明类型的对象的所有其他访问,对象的有效类型只是用于访问的左值的类型。
这有效地使用原始分配的内存为任何类型在C中定义良好的行为。我试图在C ++标准文档中找到类似的段落,但找不到一个。 我认为C ++在这方面有不同的方法。 什么是C语言中“没有声明类型的已分配对象”的C ++等价物,C ++标准如何定义它?
我认为,C ++在这方面的方法可以概括为:“ malloc()
是邪恶的。使用new
代替。没有声明类型的对象就没有了。” 当然,C ++实现需要定义全局operator new()
,它基本上是malloc()
的C ++版本(可以由用户提供)。 这个运算符的存在certificate了C ++中没有声明类型的对象,但标准不会承认它。
如果我是你,我会采取务实的态度。 全局operator new()
和malloc()
都可以在C ++中使用,因此任何实现都必须能够合理地使用它们的返回值。 特别是malloc()
在C和C ++中的行为相同。 因此,只需处理这些无类型对象,就像在C中处理它们一样,你应该没问题。
对于C ++,这在Object lifetime [object.life]中描述; 特别是:
类型
T
对象的生命周期始于:
- 获得具有适当对齐和
T
型尺寸的存储,并且- 如果对象具有非平凡的初始化,则其初始化完成。
生命周期一直持续到存储被重用或对象被破坏为止:
程序可以通过重用对象占用的存储来结束任何对象的生命周期,或者通过使用非平凡的析构函数显式调用类类型的对象的析构函数来结束任何对象的生命周期。
这具有相当奇怪的含义,即未使用的分配存储(从operator new
返回)包含适合该存储块的每个普通类型的对象,至少直到使用存储块为止。 但是,标准更关心的是如何正确治疗非平凡类型,而不是这种轻微的疣。
我认为“没有声明类型的已分配对象”是有效分配的存储,尚未初始化,并且尚未在该存储空间中创建任何对象。 从全局分配函数::operator new(std::size_t)
和family(§3.7.4/ 2);
§3.7.4.1/ 2
分配function尝试分配所请求的存储量。 如果成功,它将返回存储块的起始地址,其长度以字节为单位应至少与请求的大小一样大。 从分配函数返回时,分配的存储的内容没有限制。
对象的创建,无论是自动的还是动态的,都由两个阶段控制,即分配本身,然后是该空间中对象的构造。
§3.8/ 1
对象的生命周期是对象的运行时属性。 如果一个对象属于类或聚合类型,并且它或其成员之一由除了普通默认构造函数之外的构造函数初始化,则称该对象具有非空的初始化。 [ 注意:通过简单的复制/移动构造函数初始化是非空的初始化。 – 结束注释 ]类型
T
对象的生命周期从以下开始:
- 获得具有适当对齐和
T
型尺寸的存储,并且- 如果对象具有非平凡的初始化,则其初始化完成。
与此相对应;
类型T的对象的生命周期在以下情况结束:
- 如果
T
是具有非平凡析构函数(12.4)的类类型,则析构函数调用将启动,或者- 对象占用的存储器被重用或释放。
C ++ WD n4527 。
我不确定这样的模拟是否存在,或者在C ++中是否需要。 要分配内存,您可以执行以下三种操作之一
Foo f;
这将在堆栈上分配sizeof(Foo)
内存量。 大小在编译时是已知的,这是编译器知道要分配多少空间的方式。 数组等也是如此。
另一种选择是
Foo* f = new Foo; // or the smart pointer alternatives
这将从堆中分配,但必须再次知道sizeof(Foo)
,但这允许在运行时进行分配。
正如@BasileStarynkevitch所提到的,第三个选项是新的位置
您可以看到C ++中的所有这些分配机制都需要了解您为其分配空间的类型 。
虽然可以在C ++中使用malloc
等,但它却与典型的C ++语义相悖。 您可以看到对类似问题的讨论。 分配“原始”内存的其他机制是hacky。 例如
void* p = operator new(size);
这将分配size
字节数。