c++中与'分配对象(没有声明类型')等价的是什么?在C

What is the C++ equivalent of an 'allocated object having no declared type' in C?

本文关键字:类型 是什么 分配 中与 对象 c++ 声明      更新时间:2023-10-16

我正在用c++为我的VM写一个内存管理器。嗯,更确切地说,VM指令将被编译成带有嵌入式内存管理器的c++。我在处理C语言方面更舒服了,但现在我确实需要本地支持异常处理,这几乎是我使用c++的唯一原因。

C和c++都有严格的混叠规则,即不兼容类型的两个对象不能重叠,C中有一个小例外,即联合。但是为了定义诸如malloc, calloc, alloca等内存分配函数的行为,C标准有以下段落:

6.5-6访问其存储值的有效类型是该对象声明的类型(如果有的话)。分配对象没有声明的类型。如果一个值被存储到一个没有通过具有非字符类型的左值声明类型类型的有效类型,则左值的类型成为对象用于该访问和后续不修改的访问存储值。如果使用memcpymemmove将值复制到没有声明类型的对象中,或者作为字符类型的数组复制,则该访问和后续不修改该值的访问的修改对象的有效类型是复制该值的对象的有效类型(如果有)。对于对没有声明类型的对象的所有其他访问,对象的有效类型只是用于访问的左值的类型。

这有效地使任何类型使用原始分配的内存在C中定义良好的行为。我试图在c++标准文档中找到类似的段落,但找不到。我认为c++在这方面有不同的方法。c++中与"没有声明类型的已分配对象"等价的是什么? c++标准是如何定义它的?

对于c++,这是在对象生命周期[Object .life];特别是:

类型为T的对象的生存期开始于:

    对于T类型,获得了正确对齐和大小的
  • 存储,并且
  • 如果对象具有非平凡初始化,则表示该对象初始化完成。

生命周期持续到存储被重用或对象被销毁:

程序可以通过重用对象所占用的存储空间或显式地调用对象来结束任何对象的生命周期使用非平凡析构函数调用类类型对象的析构函数。

这有一个相当奇怪的含义,即未使用的已分配的存储(从operator new返回)包含适合该存储块的每种平凡类型的对象,至少在使用该存储块之前。但是,标准更关心的是如何正确治疗非平凡类型的疣而不是小疣。

我不确定是否存在这样的类比,或者c++中是否需要这样的类比。要分配内存,您可以执行以下三种操作之一

Foo f;

这将在堆栈上分配sizeof(Foo)的内存量。大小是在编译时知道的,这是编译器知道要分配多少空间的方式。数组也是如此。

另一个选项是

Foo* f = new Foo;  // or the smart pointer alternatives

这将从堆中分配,但同样必须知道sizeof(Foo),但这允许在运行时分配。

第三个选项,正如@BasileStarynkevitch提到的,是放置new

可以看到 c++中的所有这些分配机制都需要了解为分配空间的类型。

虽然可以在c++中使用malloc等,但它不受欢迎,因为它违背了典型的c++语义。你可以看到对类似问题的讨论。分配"原始"内存的其他机制都很粗糙。例如

void* p = operator new(size);

这将分配size字节数

我认为"一个没有声明类型的已分配对象"有效地分配了尚未初始化且尚未在该内存空间中创建对象的存储。从全局分配函数::operator new(std::size_t)和family(§3.7.4/2);

§3.7.4.1/2

分配函数尝试分配请求的存储量。如果成功,它将返回一个存储块的起始地址,该存储块的字节长度至少与请求的大小一样大。从分配函数返回时,对分配的存储空间的内容没有限制。

对象的创建,无论是自动的还是动态的,都由两个阶段控制:分配本身和在该空间中构造对象。

§3.8/1

对象的生存期是对象的运行时属性。如果对象是类类型或聚合类型,并且它或它的一个成员是由普通默认构造函数以外的构造函数初始化的,则该对象被称为具有非空初始化。[注意:通过简单的复制/移动构造函数进行初始化是非空初始化。- end note] T类型对象的生命周期从以下时刻开始:

    T类型的正确对齐和大小的
  • 存储被获得,并且
  • 如果对象具有非平凡初始化,则表示该对象初始化完成。

相应;

类型T的对象的生命周期结束于:

  • 如果T是具有非平凡析构函数(12.4)的类类型,则开始析构函数调用,或
  • 对象占用的存储空间被重用或释放。

c++ WD n4527。

我认为,c++在这方面的做法可以概括为:" malloc()是邪恶的。使用new代替。没有声明类型的对象是不存在的。"当然,c++实现需要定义全局operator new(),它基本上是malloc()的c++版本(并且可以由用户提供)。这个操作符的存在本身就证明了c++中存在一些类似对象而没有声明类型的东西,但标准不承认它。

如果我是你,我会采取务实的方法。全局operator new()malloc()在c++中都是可用的,所以任何实现都必须能够合理地使用它们的返回值。特别是malloc()在C和c++中的行为是相同的。因此,只要像在C中处理它们一样处理这些未类型化对象,就应该没问题了。