C++ NULL 指针和 const 正确性

Posted

技术标签:

【中文标题】C++ NULL 指针和 const 正确性【英文标题】:C++ NULL pointers and const correctness 【发布时间】:2013-11-26 10:31:26 【问题描述】:

我读到最好在删除指针数据成员后检查类的析构函数,如下所示:

if( 0 != m_pPointer)

    delete m_pPointer;
    m_pPointer= 0;

但是,我发现这会阻止您将 const 指针声明为数据成员,如下所示:

Type* const m_pPointer;

NULL 分配给指针(如我上面的示例)不是 const 正确性的障碍吗? 最好的方法是什么?保留所有 const 并停止将 NULL 分配给已删除的指针或声明 non-const 指针,即使它们的地址永远不会改变?

【问题讨论】:

我不同意这是一个好习惯。双重删除指针几乎总是应用程序中的逻辑错误,删除后将指针设置为 null 会隐藏该错误。如果不设置为null,应用程序可能会崩溃,这是一件好事。 您是否有一个用例,将指针设置为0 实际上有帮助?我以前见过这种模式,但除非您导出引用指针,否则我看不到它有帮助。 @JasonSwartz 不,它不会在删除 null 指针时崩溃。 delete p; 其中p 为 null 由语言定义为无操作。有关更多信息,请参阅operator delete 问题是问如何以最少的错误方式使用错误的工具。为什么不使用智能指针? @JasonSwartz:如果指针不共享,而析构函数中只有deleted,是的,很难意外导致双重删除。另一方面,如果是这样的话,通过将指针重置为null 来使代码更复杂是没有意义的——我们在析构函数中,之后就再也看不到指针了! (除非在堆/堆栈损坏的情况下) 【参考方案1】:

这是不好的做法,原因如下:

    在析构函数中将指针设置为 null 可能会掩盖双重破坏问题。最好的做法是尽早发现问题。 在deleteing 之前检查指针是否为空只会添加不必要的代码。 delete 什么都不做来处理空指针。最好的做法是尽量减少代码量。

【讨论】:

同意。 if 支票被浪费了,是不必要的负担。 那么在我们的代码中是否可以使用delete m_pPointer; m_pPointer= 0; 这样的结构?将 NULL 分配给指针可确保如果稍后在该指针上调用某些内容,则程序将崩溃。这不是好事吗? @JasonSwartz 您是否打算展示稍后如何在该指针上调用某些内容的示例?如果你能做到这一点,你会发现它非常做作,而且设计很糟糕。无论如何,您只会将问题转移到其他地方。 @juanchopanza 同样,设置为 null 的目的是及早发现设计不佳...如果每个人都编码完美,则不需要。唉,事实并非如此。 顺便说一句,这至少是在 STL 中为向量所做的(Visual Studio 10)...指针设置为 NULL,一切都被清除了。【参考方案2】:

删除空指针是保证安全的,所以空检查是没有意义的。

如果一个类有一个成员是指向非 const 对象的 const 指针,那么你是说指针值在包装对象的生命周期内不会改变 - 在这种情况下你应该只在在这种情况下,指向的对象将与包装对象一样长或更长,并且包装对象永远不想指向不同的对象。

你有这个问题只是意味着你在错误的地方使用了一个 const 指针。您声称在您的情况下指针值永远不会改变,但在您的示例中它显然会改变 - 它更改为 null。

【讨论】:

问题的关键是值是否应该变为null。答案是,不,不应该,应该是 const。 可能,在您的实际情况下,它不能从 sn-p 派生。也就是说,当一个成员指针在包装对象的生命周期中被多次删除和重新分配时,它可以始终确保指针指向一个对象或指向空值,然后重新创建该对象可以简单地delete 然后new。如果指向的对象在初始化列表中实例化并在析构函数中销毁,则无需将其设置为 null,它可以是 const 指针 - 甚至更好的是直接将其放入堆栈。【参考方案3】:

“最好的办法”是:

class foo 
  std::unique_ptr<bar> m_pPointer;
public:
  foo(std::unique_ptr<bar> pPointer)
    : m_pPointerstd::move(pPointer) 
;

或者对于 const,

class foo 
  const std::unique_ptr<bar> m_pPointer;
public:
  foo(std::unique_ptr<bar> pPointer)
   : m_pPointerstd::move(pPointer) 
;

没有new,没有delete,没有析构函数。

【讨论】:

【参考方案4】:

当您将静态库与来自两个不同共享库(在 Linux 上)的全局或静态对象链接时,可能会导致奇怪的情况,这些共享库稍后会链接到同一个可执行文件。

每个共享库对象都插入对构造函数和析构函数的调用,因此您将有一个对象以及对同一对象的构造函数和析构函数的两次调用(实际上您将有 2 个对象映射到相同的地址)。

当您的应用在第二个析构函数中崩溃时,您可能会发现问题。 如果你 NULL 它你永远不会知道有问题。

对于您的问题:除了上述问题,我认为您应该区分两种类型的指针: 请参阅下面的课程:

class A
  obj *x, *y;
  A()
    x = new obj;
    y = NULL
  
  ~A()
    delete x;
    if(y)delete y; // the `if` here will save the calling and returning run time when NULL. 
  
  void RecicleX()
    delete x;
    x = new obj;
  
  void InitY()
    assert(y==NULL); //illegal to call init when already
    y = new obj;
  
  void TermY()
    assert(y); //illegal to call term when already inited
    delete y;
    y = NULL; //prevent crush in dtor if called after...
   
;

x 总是存在的,所以不需要检查它,也不需要将它归零。 y 可能存在也可能不存在,所以我认为您应该在删除后将其设为空。 (您可能还想知道当前状态,例如assert

【讨论】:

以上是关于C++ NULL 指针和 const 正确性的主要内容,如果未能解决你的问题,请参考以下文章

C++语法(指针和引用的区别)

C++ const 正确性漏洞或意外使用?

c++重要的概念

C++杂谈const限定符与const指针

C++ const 常量指针

初始化指向结构 c++ 的 const 指针