【问题标题】:What are the reasons to allocate a pointer on the heap?在堆上分配指针的原因是什么?
【发布时间】:2018-02-07 20:51:20
【问题描述】:

可能已经有人问过这个问题,但我找不到。如果您看到了什么,请重定向我。 问题: 使用有什么好处:

myClass* pointer;

结束

myClass* pointer = new(myClass);

通过阅读其他主题,我了解到第一个选项在堆栈上分配一个空间并让指针指向它,而第二个选项在堆上分配一个空间并让指针指向它。 但是我还读到第二个选项很乏味,因为您必须使用删除来释放空间。 那么为什么要使用第二种选择。 我是菜鸟,所以请详细解释。

编辑

#include <iostream>

using namespace std;

class Dog
{
        public:
                void bark()
                {
                        cout << "wouf!!!" << endl;
                }

};


int main()
{

        Dog* myDog = new(Dog);
        myDog->bark();
        delete myDog;
        return 0;

}

和

#include <iostream>

using namespace std;

class Dog
{
        public:
                void bark()
                {
                        cout << "wouf!!!" << endl;
                }

};


int main()
{

        Dog* myDog;
        myDog->bark();
        return 0;

}

编译并给我“wouf!!!”。那么为什么要使用“new”关键字呢?

【问题讨论】:

  • 第一个选项除了指针本身不分配任何东西。该存储的位置取决于使用情况。我认为您的第一个示例中可能有一个额外的*。
  • 堆栈和堆是语言不关心的实现细节。您要问的是自动与动态存储持续时间。
  • 可以给你指点书单吗:stackoverflow.com/questions/388242/…
  • 栈是有限的,有时几kB,有时2-8MB,所以如果你有很大的图片,或者动态增长的列表,你可能需要使用堆。当您从函数返回时,堆栈也会消失,这对于您想要保留的东西来说是个问题。

标签: c++ class pointers dynamic-memory-allocation static-memory-allocation


【解决方案1】:

我了解第一个选项会在堆栈上分配空间,并且 使指针指向它,而第二个分配空间 堆并创建一个指向它的指针。

上面是不正确的——第一个选项在堆栈上为指针本身分配空间,但没有为指针指向的任何对象分配空间。也就是说,指针没有特别指向任何东西,因此没有用处(除非/直到您将指针设置为指向某物)

特别是,这段代码看起来“工作”只是纯粹的运气:

Dog* myDog;
myDog->bark();   // ERROR, calls a method on an invalid pointer!

...上面的代码调用了未定义的行为,在理想情况下它会简单地崩溃,因为你在一个无效指针上调用一个方法。但是 C++ 编译器通常更喜欢最大限度地提高效率而不是优雅地处理程序员错误,因此它们通常不会检查无效指针,并且由于您的 bark() 方法实际上并不使用来自 Dog 对象的任何数据,它能够在没有任何明显崩溃的情况下执行。尝试将您的 bark() 方法设为虚拟 OTOH,您可能会看到上述代码崩溃。

第二个在堆上分配一个空间并让一个指针指向 它。

没错。

但我也读到第二个选项很乏味,因为你必须 使用 delete 释放空间。

不仅乏味,而且容易出错——很容易(在一个非平凡的程序中)最终得到一个忘记调用 delete 的代码路径,然后出现内存泄漏。或者,您最终可能会在同一个指针上调用两次 delete,然后您会出现未定义的行为,并且可能会崩溃或数据损坏。调试这两个错误都不是很有趣。

那么为什么要使用第二种选择。

传统上,当您需要对象保持有效的时间超过调用代码的范围时,您会使用动态分配 - 例如,如果您需要对象即使在您创建对象的函数之后仍然存在回来。将其与堆栈分配进行对比:

myClass someStackObject;

... 其中someStackObject 保证在调用函数返回时被销毁,这通常是一件好事——但如果您需要someStackObject 即使在您的函数返回后仍然存在,则不会。

如今,大多数人会完全避免使用原始/C 风格的指针,因为它们非常容易出错。在堆上分配对象的现代 C++ 方法如下所示:

std::shared_ptr<myClass> pointer = std::make_shared<myClass>();

... 这是首选,因为它为您提供了一个堆分配的 myClass 对象,只要至少有一个 std::shared_ptr 指向它,它的指向对象就会继续存在(好),但也将在没有 std::shared_ptr 指向它的那一刻自动被删除(更好,因为这意味着没有内存泄漏,也不需要显式调用delete,这意味着没有潜在的双重删除)

【讨论】:

  • 您好,感谢您的精彩回答。 “但没有为指针指向的任何对象分配空间”哦,我明白了,我可能很困惑:int* ptr; 是否也只是为指针本身或它要指向的 int 分配空间? (也许为此我感到困惑)。 “传统上,当您需要对象保持有效的时间超过调用代码的范围时,您会使用第二个对象”我只是尝试这样做:void foo() { Dog* myDog = new(Dog); } int main() { foo(); myDog.bark(); return 0; }
  • 哦,我只是注意到没有换行符。我不确定这是回答答案的正确方法...
  • 对于任何类型,只要您声明一个指针(例如 X* x;),您只是为指针本身分配空间。为 X 类型的实际对象分配空间必须单独完成(例如 X* x = new X(); ... 或等效的 X * x; x = new X();)
  • 在您评论中的较长范围示例中,您可能会这样做:`Dog * foo() { Dog* myDog = new Dog;返回我的狗; } int main() { 狗 * d = foo(); d->吠声();删除 d; }
  • 好吧,有道理
猜你喜欢
  • 2022-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-23
  • 2012-01-10
相关资源
最近更新 更多