通过指向常量的指针释放内存是一个好的做法吗?

10
有很多问题讨论C和C ++处理指向常量的删除的细节,即free()不接受它们,而deletedelete[]则接受,且const性质不会防止对象被销毁。
我感兴趣的是你是否认为这样做是一个好习惯,而不是语言(C和C ++)允许什么。
指向const的删除的争论包括:
  • Linus Torvalds的 kfree()不像C的 free()那样,需要 void const * 参数,因为他认为释放内存不会影响所指向的内容。
  • free()在const关键字引入之前设计。
  • C ++的删除操作符允许删除const数据。
反对其进行指向const的删除的论点包括:
  • 程序员不希望在将指向const的指针传递给它时修改(或删除)数据。
  • 许多人认为指向const的指针意味着不获取数据的所有权(但非const则意味着获取所有权)。
  • 这是大多数库和现有代码中看到的常见实践。
请在您的回答中充分说明,并可能参考权威资料。我的意图不是在这里发起投票。

对我来说,接受一个 非 const 指针意味着一个输出参数。如果它不是更新对象而是销毁对象,我同样会感到惊讶。 - visitor
2
@nobugz,许多有价值的“最佳实践”问题在某种程度上不是很主观吗?因为答案(如果有的话)很大程度上取决于实践/偏好的统计分布(因此,意见)? - mlvljr
@visitor,通过非const指针参数转移所有权是很常见的。所有智能指针都在它们的构造函数和可能的重置函数中这样做,而且通常也直接在应用程序中看到(通常作为构造函数的参数 - 这经常是一个潜在的内存泄漏问题,但这是另一个问题)。 - Tronic
@mlvljr:提出主观问题和提出促进争论的主观问题是有区别的。也许你错过了那个被删除的答案,它在4条评论之内就变成了人身攻击的指责——这相当具有争议性。 - Roger Pate
在我看来,一个删除指针的函数应该有一个能够表明其作用的名称:DeleteX、DestroyX、FreeX、ReleaseX。无论参数是否为const都似乎不会影响这一点。如果函数名没有表明这一点,我就没有理由期望void FooBar(X*)会承担所有权并释放对象。至于智能指针:如果只接受const指针,那么对可变对象使用智能指针会很棘手,不是吗? - visitor
@visitor,您忽略了一种常见情况,即通过构造函数将所有权传递给类。在这种情况下,函数显然不应命名为Delete/Destroy/Free,并且可能很难通过函数/类命名来暗示所有权的转移。 - Tronic
4个回答

4

使用适当的策略结束对象的生命周期是一个好习惯。对于动态对象,这意味着删除您新建的内容,释放您malloc的内容等等。无论该对象是否为const,都不会影响其生命周期是否应该结束。

常量或易失性是存在于对象生命周期内的属性,并且随着delete-expression或调用free而结束。无论您对此持什么观点或其他语言如何工作,这就是C++对象模型的工作方式。一个简单的例子来展示这一点是语言如何将delete-expressions转换为operator delete调用:

#include <new>
void* operator new(std::size_t size);
void operator delete(void* p);

int main() {
  delete new int(); // int* to void*, since this is also an allowed
  // implicit conversion, it may not be clear what is happening

  // however, these are clearly not implicit conversions:
  delete new int const();          // int const         * to void*
  delete new int volatile();       // int       volatile* to void*
  delete new int const volatile(); // int const volatile* to void*
}

还有一个例子,可能不太清晰,就是为什么不能在const上重载构造函数:

struct S {
  S() const; // not allowed
};

一个对象只有在创建后(即其生命周期开始时,当构造函数正常返回时发生)和销毁前(即其生命周期结束时,当进入析构函数时发生)才是const。在该生命周期之前或之后,您可能拥有类型为T const *的指针(例如),但它不指向对象,解引用它将导致未定义行为。
相同的推理适用于C语言,但您必须考虑到C语言已经有大约40年的历史,并且在很大程度上保持了一致性。
(我认为这个问题是主观和有争议的,会投票以这种方式关闭它,除非我显然帮助了spark讨论;因此作为CW回答。)

3

我从未真正理解反对删除(或释放)常量指针的论点。更确切地说,我有些能够理解这种理性,但在我的看来,它们同样适用于对象中的常量成员或块中的常量变量,但我从未见过有人争辩说当包含对象被删除或执行离开它们所包含的块时,那些应该不被销毁并且其内存被释放。

管理对象的逻辑可变性(即const还是非const)和管理它们的生命周期(即使用对象变量、智能指针--哪一个?--或原始指针)似乎与我无关。

换句话说,如果

{
    Foo const x;
    ...
}

是有效且良好的样式。为什么会这样呢?

{
    Foo const* xptr = new Foo;
    ...
    delete xptr;
}

不使用智能指针而是使用原始指针并不是好的编程风格,但这是另一个问题。


1
@Poita_,在我看来,所有权问题和逻辑可变性问题是相互独立的。我不明白为什么我应该保持一个指向不可变对象的非 const 指针,只是因为我想在以后能够销毁该对象。要能够销毁对象,你必须是所有者,但这并不意味着你必须能够修改它。虽然让所有者实体都不能修改对象而非所有非所有者实体都能修改对象可能是有问题的,但拥有一个直到销毁之前没有人可以修改的对象是有意义的,因为这样就有了两种所有者,一种能修改,一种不能修改。 - AProgrammer
我同意,但我认为那就是被问到的问题。 - Peter Alexander
@Poita_: 这是一个所有权问题,而不是常量性问题。如果foo接管了xptr的所有权,则foo负责释放它。 - Roger Pate

2

好的,以下是一些相关的内容,可能太长无法放入评论中:

  1. 一段时间以前,通过指向常量的指针释放内存的做法被明确禁止,参见this dr. Dobb's article, the "Language Law" ( :)) part

  2. http://groups.google.ru/group/comp.lang.c++.moderated上曾有两次相关讨论:"Delete a const pointer?""Why can operator delete be called on a const pointer"(两者都实际上涉及到了本例中的常量指针情况)。

  3. 我的观点(因为你要求提供论据):在任何给定上下文中,操作的可能性都由类或函数的(在文档中明确或隐含定义的)契约来定义,而不是仅仅由方法签名或参数类型来定义。


0

常量性和生命周期是两个不同的概念。如果对象的所有者决定该对象没有存在的理由,我认为释放一个常量对象没有问题(就像一个局部的const对象在超出作用域时会被“释放”一样)。

至于free()不接受const指针,我认为可以争论标准委员会可能疏忽了这一点,或者因为它接受与malloc()返回的相同类型的指针。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接