如果我这样做:
// (1.)
int* p = new int;
//...do something
delete p;
// (2.)
class sample
{
public:
sample(){}
~sample(){}
};
sample* pObj = new sample;
//...do something
delete pObj;
那么C ++编译器如何知道delete
之后的对象是内置数据类型还是类对象?
我的另一个问题是,如果我new
指向int
的数组然后是delete []
,那么编译器如何知道要取消分配的内存块的大小?
答案 0 :(得分:4)
它知道它们之间的区别,因为你传递给它的指针的类型:传递一个不同于你分配的指针类型的未定义行为(除非你可以传递指向基类的指针,如果析构函数当然是virtual
。
数组的大小将存储在某处。就像在C中你可以malloc一定量的内存,然后释放 - 运行时必须设法知道之前分配的大小。
例如,它可以在分配缓冲区之前存储元素的数量。标准明确允许编译器在数组分配的情况下将不同的请求大小传递给分配函数(operator new[]
) - 编译器可以使用它来将计数加入,并抵消{返回的地址{1}}表达式按该计数器的大小。
答案 1 :(得分:4)
编译器知道指向对象的类型,因为它知道指针的类型:
p
是int*
,因此指向的对象将是int
。pObj
是sample*
,因此指向的对象将是sample
。编译器不知道您的int* p
是指向单个int
对象还是指向数组(int[N]
)。这就是为什么你必须记住使用delete[]
而不是delete
来表示数组。
要解除分配的内存块的大小,最重要的是,要销毁的对象的数量是已知的,因为new[]
将它们存储在某处,delete[]
知道在哪里检索这些值。 This question from C++ FAQ Lite显示了实施new[]
和delete[]
的两种常用技巧。
答案 2 :(得分:1)
它没有!
所有delete
所做的是它调用类型的析构函数,在原始类型的情况下是“无操作”。然后,它将指针传递给::operator delete
(如果您愿意,则传递过载版本),那么运算符会返回内存(内存管理器问题)。即如果您愿意,您可以使用C ++轻松编写自己的内存管理器,默认情况下该语言提供一个!
答案 3 :(得分:1)
答案 4 :(得分:1)
那么C ++编译器如何知道删除后的对象是内置数据类型还是类对象?
因为在编译时编译器会跟踪每个对象的类型并植入适当的代码。
我的另一个问题是,如果我新建一个指向int数组的指针,然后我删除[]那么编译器如何知道要取消分配的内存块的大小?
没有。这由运行时系统跟踪 当您动态分配数组时,运行时库将对象的大小与对象相关联,因此当它删除它时,它知道(通过查找关联的值)大小。
但我想你想知道这种关联是怎么回事? 这取决于系统并且是实现细节。但是一个简单的策略是在前四个字节中分配额外的4个字节存储大小,然后返回指向分配的第4个字节的指针。删除指针时,您知道大小是指针前的4个字节。注意:我并不是说你的系统正在使用这种技术,但它只是一种策略。
答案 5 :(得分:0)
对于问题的第一个(非数组)部分,上面的答案表明编译器根据指针类型插入代码来解除分配适当数量的字节,但不能为我提供明确的答案...删除运算符1)调用析构函数(如果适用)然后2)调用“operator delete()”函数...它是实际取消分配的运算符删除。我可以看到编译器生成的代码在第(1)部分中起作用,即。必须插入析构函数的目标地址。但在第(2)部分中,它是一个处理解除分配的预先存在的库函数,那么它将如何知道数据的大小?全局运算符delete - 我认为除非程序员定义了类成员/重载全局版本,否则它在所有情况下都使用 - 只接受一个void *参数指定数据的开头,所以它可以甚至传递数据大小。 我已经读过指示编译器生成的代码构思的东西,以及暗示非数组的全局运算符delete只使用free()的东西,即。它不是通过指针类型知道数据大小,而是通过在数据本身之前查看几个字节,其中大小将被new / malloc隐藏。后者是唯一对我有意义的解决方案,但也许有人可以用不同的方式启发我......