删除 nullptr 可能会也可能不会调用释放函数。为什么不保证后者?

问题描述 投票:0回答:2

C++20 标准说(见[expr.delete]

如果delete-expression的操作数的值为空指针值, 未指定是否会如上所述调用释放函数。

cppreference.com 说(见删除表达式

如果 expression 的计算结果为空指针值,则不会调用任何析构函数, 并且可能会或可能不会调用释放函数(未指定), 但默认的释放函数保证什么都不做 当传递一个空指针时。

如果 delete-expression 的操作数为 null,为什么编译器会调用释放函数?

如果保证不会调用释放函数,规则会更简单,但我认为标准允许这样做是有充分理由的。

c++ language-lawyer compiler-optimization delete-operator
2个回答
2
投票

从历史的角度来看,标准曾经在

c++03 §5.3.5/2

[...] 如果 delete 的操作数的值为空指针,则操作无效。

这个写法被发现有问题,引发了缺陷报告

标准没有指定术语“无效”。从上下文中不清楚,是否要求调用的释放函数无效,或者 delete-expression 不得调用释放函数。

由于编译器以不同的方式解释旧的、模棱两可的措辞(参见这个例子,其中 GCC 7.5 和 clang 7.0 对是否插入此检查有不同意见),不指定是最兼容的决定。

从优化器的角度来看,这也是可取的,因为它可以根据情况提供优化机会,例如:

  • 插入空检查以避免函数调用,如果这是常见情况,
  • 如果已知指针值为空,则省略函数调用,
  • 如果 null 不太可能,则删除检查。

1
投票

在表达式

delete pT;
中,需要发生两件事。需要调用
pT
指向的对象的析构函数。并且需要调用释放函数。

但是,如果

pT
nullptr
,那么你不能做第一个,你知道第二个是空操作。因此,编译器在概念上将此表达式转换为具有以下效果的东西是有意义的:

if(pT)
{
  pT->~T();
  deallocation_function(pT);
}

注意如果

pT
通过,那么它会被检查两次:一次在这里,一次在释放函数中。但是由于我们需要在调用析构函数之前在这里检查它,所以双重不可避免,因为编译器不拥有释放函数。

但是,如果

T
是微不足道的可破坏的(因此不会执行任何实际代码),编译器不需要调用析构函数。这意味着条件检查现在完全多余。所以,一个聪明的编译器可以优化它。在这些情况下,将使用 nullptr 值调用释放函数。

这一切都基于释放函数将始终执行 nullptr 检查的假设。这是所有标准库释放函数所做的,并且对大多数此类函数都有意义。

© www.soinside.com 2019 - 2024. All rights reserved.