【问题标题】:Error when deleting these pointers删除这些指针时出错
【发布时间】:2017-10-02 01:44:14
【问题描述】:

我知道当我用 new 初始化一个指针时,我需要删除指针,否则会发生内存泄漏。我有一个这样设置的课程:

class foobar
{
private:

    //! Pointer to the global nodal list
    int *p_test1;

    //! Pointer to the global block label list
    int *p_test2;

public:
    foobar(int *test1, int *test2)
    {
        p_test1 = test1;
        p_test2 = test2;
    }

    ~foobar()
    {
        delete p_test1;
        delete p_test2;
    }
};

现在,当调用析构函数时,程序崩溃并且控制台显示 Program exited with return code -6。

调试窗口指出:

程序接收信号SIGABRT

当我通过调用堆栈时,最后一项是析构函数。堆栈似乎试图释放内存但失败了。

我想知道为什么会这样?使用此类设置释放内存的首选方法是什么。

附带说明,如果我在析构函数中注释掉代码,那么当 foobar 类的实例完成时,指针当然不会被释放。但是,当我调用该类的另一个实例时(假设该实例的创建是由用户按下 GUI 上的按钮决定的),程序就会崩溃。再次,这是为什么?我感觉这与没有正确销毁指针有关。

【问题讨论】:

  • 你真的分配内存了吗?您在代码中哪里使用了 new 运算符?
  • 在析构函数中使用指针的delete 意味着所有权属于该对象。现在我们只看到您正在传递一个指向对象的指针并存储它,谁分配了它?相关的new在哪里?
  • 在代码中,我正在调用一个函数,该函数返回指向对象的指针。对象本身不是指针。该对象由另一个类拥有。当这个析构函数发生时,我不想删除该对象。我只想将这些指针重置回“零”或删除它们
  • 不要用散文描述你的代码。显示minimal reproducible example
  • 这是一种非常奇怪的 C++ 代码设计,与现代 C++11 之后的源代码应有的样子相去甚远。考虑根本不使用new/delete,如果你不能避免它(通常你应该能够轻松地使用良好的整体架构),那么我宁愿将new + delete 对应的对保留在同一源中,彼此靠近。此时您有new who-knows-where 和delete 这里,这将使阅读+审查此类源变得困难,不得不使用大量推理来查看每个new 是否与@987654330 配对@ 正确(显然它已经不是,因为你会崩溃)。

标签: c++ pointers


【解决方案1】:

正如cmets中提到的,这个例子中没有new,所以很难解释newdelete之间的平衡。

规则是,如果你新建一个指针,你应该删除它。

int * p = new int( 5 );

std::cout << "The value of p is " << *p << std::endl;

delete p;

但是,如果你使用一个对象,因为你没有新建它,你不应该(通常)删除它。

void somefunction( int * p )
{
    std::cout << "The value of p is " << *p << std::endl;

    delete p; // this looks smelly - deleting a pointer which was allocated.
}

为了尝试简化这个问题(我应该删除哪些指针),现代 C++ 有一些很好的指导方针。

资源获取是初始化

RAII 将资源放入一个类中。完整的类(与指针相反)具有非常清晰的生命周期。当它因为超出范围而被销毁时,就会调用析构函数。

 class PointerHolder {
      int * mp;
   public:
      PointerHolder( int value ) : mp( NULL) {
          mp = new int( value );
      }
      ~PointerHolder() {
          delete mp;
      }
 }

虽然上面的例子违反了 5 的规则。

5 规则(或零)

上面的 PointerHolder 的问题是它没有实现所有 5 个特殊运算符。

PointerHolder a( 12 );
PointerHolder b( 10 );

a = b;  // What is going to happen?????

a获取b的指针的值,第二次销毁时,就是第二次删除同一块内存,程序崩溃。

5 个状态的规则,如果您实施 destructormove operatorcopy constructoroperator =,您应该实施(或删除所有这些)。

在这种情况下,删除移动和复制运算符,将生成一个简单的unique_ptr

零规则是,如果一个类实现了所有运算符,或者一个都不实现,那么它的可重用性更高。

如果您只需要实现一些运算符,则很可能您没有识别“资源” - 它应该是一个包含所有 5 个重载运算符的单个类。

现代 C++

自 C++11 起,new 和 delete 的使用被认为仅限于库编写者。由于std::shared_ptrstd::unique_ptr 的使用为您管理new delete 生命周期,因此您可以专注于手头的问题。

【讨论】:

    猜你喜欢
    • 2013-04-14
    • 2019-03-14
    • 2014-02-14
    • 1970-01-01
    • 1970-01-01
    • 2020-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多