【问题标题】:Pointer Deallocation and Heap memory, c++指针释放和堆内存,C++
【发布时间】:2016-03-06 10:42:11
【问题描述】:

我有一个我调用的成员函数,从那里我得到一个指向 BankAccount 类的私有成员的指针,我不确定当我解除分配指针时会发生什么。我创建了一个新的指针和堆内存地址,然后将指针分配给其他东西。 “删除”最终会删除什么?

我读到如果你删除一个指针

这里是代码

void Employee::raise(){

    BankAccount* tempBank = new BankAccount(); //set pointer to new heap place

    double amt;

    tempBank = get_bank(); // set pointer to existing heap implicitly
    amt = tempBank->get_amount();
    amt = (amt + (amt/12));
    tempBank->set_amount(amt);


    delete tempBank; //does this delete new heap created or does it add a new block of
                     //memory to heap since it is trying to delete a pointer assigned 
                     //by memory address
    tempBank = NULL;
}

我意识到我可以执行下面的代码来避免这种情况,但现在我很好奇在上述情况下内存会发生什么

BankAccount* tempBank = get_bank();

那么在我原来的情况下调用 delete 时究竟会发生什么?

【问题讨论】:

  • 顺便说一句,在它消失之前将tempBank 设置为NULL 是没有意义的。

标签: c++ pointers memory memory-management heap-memory


【解决方案1】:

当你使用delete ptr;时,ptr指向的对象被销毁,相应的内存返回给内存管理系统。变量ptr 及其任何副本都包含一个指向现在无法访问的内存的位模式(系统可能仍允许您实际访问此内存,它甚至可能仍包含原始数据,但这是未定义的行为,您不应依赖它)。

换句话说,内存释放不会影响指针,但会影响指向的实体。在您的情况下,BankAccount,即*tempBank 的结果被破坏,而指针tempBank 通过delete 操作保持不变。显然,将tempBank 设置为NULL 确实会更改此特定指针,但不会更改其他副本(如果有的话),给您错误的安全感:除非我保留,否则我不会将deleted 指针设置为NULL无论出于何种原因,他们都在附近......

【讨论】:

  • 我不同意:如果您必须使用原始指针,将它们设置为 NULL/nullptr 是明确将它们标记为“不指向任何合理的东西”的唯一方法,根据我的经验,如果您遇到了取消引用问题。而且您永远不知道其他人最终会如何修改您的代码(并重用您从未打算重用的指针)
  • @mvd:请注意,tempBank 在设置为NULL 后立即超出范围。那有什么意义呢?
  • 在这种情况下你是对的。但也许一段时间后这个功能会被扩展,可能是由其他不注意的人。此外:我发现拥有良好的默认习惯会更安全。我不想考虑指针是否仍然可以在其他地方使用,然后才重置它。在压力大的阶段我肯定会忘记一个,当我负担不起的时候肯定会咬我;-)无论如何,我希望我们都同意拥有原始指针是当代 C++ 中的一种反模式。
  • @mvd:关键问题实际上是类似于T* ptr2 = ptr1; ... delete ptr2; ptr2 = 0; 的代码事实上ptr2 设置为null 不会影响ptr1。也就是说,当您处理指针时,您需要知道哪些指针在哪里有效。将一些指针设置为 null 会给人一种错误的安全感。我确实同意在大多数情况下拥有原始指针是一个坏主意(显然,在实现资源管理器时它们仍然会出现)。
【解决方案2】:

指针本质上只是属于它们指向的数据结构的内存中第一个字节的地址。所以在你的情况下:

BankAccount* tempBank = new BankAccount(); // this creates a new object of type BankAccount
                                           // the pointer tempBank points to the first byte of that object in memory

tempBank = get_bank();  // now tempBank points to the first byte of whatever is returned from get_bank()
                        // that means that you no longer know the address of the object you created above (tempBank now points to something different)
                        // C++ has no garbage collection, so you just leaked that memory

delete tempBank; // you delete the object that was returned from get_bank
                 // so that memory is now marked as free and can be reused by whatever needs it

tempBank = NULL;  // this is good style, you should always do it, but it does nothing to any allocated memory

顺便说一句:在现代 C++ 中,使用普通的 new 和 delete 并拥有原始指针被认为是不好的风格。您可能需要考虑使用 std::shared_ptr 或 std::unique_ptr (如果您还不能使用 C++11,则使用它们的 boost 等效项)

【讨论】:

  • 糟糕,抓住了我。我的回答不准确:尽管有很多 C++ 开发人员实际上认为所有原始指针都是不好的,但恕我直言,应该避免 拥有原始指针。我编辑了答案,谢谢这个问题。主要原因之一是,您必须在某处手动删除它们,这很容易被遗忘 - 这会导致内存泄漏。或者你在同一个指针上调用了两次 delete,这会使你的程序崩溃。为了进一步阅读,我推荐 Scott Meyer 的“Effective Modern C++”,第 4 章。
  • 我个人认为 non-owning 原始指针很好,我很高兴 Bjarne Stroustrup 本人也同意这一观点(youtube.com/watch?v=1OEu9C51K2A 来自大致24:00)
【解决方案3】:

我发现这些信息可能对您有用:

普通 delete 释放 ptr 指向的内存块(如果不为 null),释放先前通过调用 operator new 分配给它的存储空间,并使该指针位置无效。

您可以在原始网址中找到更多信息:http://www.cplusplus.com/reference/new/operator%20delete/

【讨论】:

    【解决方案4】:
    delete tempBank;
    

    当在指针上调用 delete 时,它​​会释放该变量 [tempBank] 指向的内存。

    C++中有两个delete的概念:一个是操作符,声明为::operator delete(void*),它基本上只是释放内存,大多数程序员通常不会想到。另一个是删除表达式,delete p;,其中 p 是一个 T*。该表达式调用 p 指向的对象的析构函数(然后释放内存),这是 C++ 的一个关键语言特性,在 C 中没有类似物。

    【讨论】:

      【解决方案5】:

      首先,你是从堆中分配你的第一个 exp 的

      BankAccount* tempBank = new BankAccount();
      

      你丢失了它的地址,通过

      将另一个对象地址分配给 tempBank 指针
      tempBank = get_bank();
      

      实际上,当您delete tempBank; 时,您实际上删除了您在函数get_bank() 中分配的对象。也因为你丢失了你用new BankAccount()分配的对象的地址,没有更多的办法删除它,因为你不知道这个对象的地址。

      【讨论】:

        【解决方案6】:

        在您的问题中,您确定get_bank() 确实返回指向在堆上分配的对象的指针(而不是堆栈上的普通对象的地址)。您没有明确提及,因此值得再次确认。现在,回到问题上来,如果 get_bank() 返回指向私有成员的指针,假设它不在堆上 - 在这种情况下,执行 doing tempBank 将导致未定义的行为,因为您只能在创建的对象上调用 delete使用new。但是,如果get_bank() 返回指向在堆上分配的对象的指针,那么它将释放该对象的内存,然后从任何其他成员函数访问该对象可能会成为一场噩梦!

        您可以查看以下链接以获取更多相关信息,

        Calling delete on variable allocated on the stack

        【讨论】:

          猜你喜欢
          • 2014-09-05
          • 1970-01-01
          • 2017-03-01
          • 1970-01-01
          • 1970-01-01
          • 2012-12-28
          • 2020-06-23
          • 1970-01-01
          • 2020-06-16
          相关资源
          最近更新 更多