繁体   English   中英

Stack vs Heap C ++

[英]Stack vs Heap C++

我刚才有一个关于堆栈变量与堆变量如何工作的快速问题。 据我了解,堆栈变量是函数返回后的变量,并且堆变量是持久的。 但我真正感到困惑的是如何在函数内部分配堆变量:

int MyObject::addObject(const char* a){
    MyObject newObject(a);
    return 0;
}

假设我有一个MyObject的构造函数,它是newObject(const char * a) 然后在调用它的函数中,在返回之后新构造的newObject被删除了吗? 如果是,那么如何在函数内分配到堆呢? 如果没有,你如何在以后清理你的记忆?

此外,析构函数的作用究竟是什么?何时被称为?

MyObject类的构造函数是MyObject() ,而不是newObject() 在您的示例中, newObject是变量的名称,而不是构造函数。

要在函数内的堆上进行分配,需要调用new运算符:

int MyObject::addObject(const char* a){
    MyObject* newObject = new MyObject(a);
    //newObject is allocated on the heap

    //... Some more code...

    delete newObject;
    //you have to explicitly delete heap-allocated variables
    //if not, you get memory leaks
    return 0;
}

你的代码:

MyObject newObject(a);

创建一个名为newObject的自动存储(堆栈) MyObject ,直到它在结束时声明它的范围 - 即关闭}

此外,析构函数的作用究竟是什么?何时被称为?

要清理内存,使用newnew[] (或malloc )分配的类并拥有它。 当对象超出自动对象的范围时,或者为动态对象显式调用delete时,都会调用它。

int addObject(const char* a){
    MyObject newObject(a);
    return 0;
}

此代码在堆栈上分配newObject。 在进入函数时构造它(在一堆堆栈内存上调用MyObject构造函数)。 当函数在从堆栈中弹出内存块之前返回时,它被破坏(MyObject析构函数调用该实例)。

MyObject *foo =  new MyObject(a);

此代码在堆上分配对象。 构造函数在分配了一些堆内存后由new运算符调用。 在您明确表示已使用delete关键字完成后,内存不会被释放并且不会调用析构函数。

delete foo;

这可以是您在计划中想要的任何地方。

堆栈内存就是你所说的,但更具体地说,它是当前作用域的本地内存。 请注意,根据您所讨论的范围,此内存可以是全局的,也可以是函数或类的本地内存。 一旦它们超出范围,它们就会被清理干净。

堆内存通过new运算符分配。 即使在指向内存的所有指针都超出范围之后,它们仍将保留在内存中。 您必须通过delete在C ++中自己显式释放此内存。 除非使用智能指针,否则将导致内存泄漏。

另外需要注意的是,请参考以下示例代码:

void foo()
{
   int *bar = new int; //creates a pointer to an int on the stack, but initializes an int on the heap
   *bar = 3; //sets the int being pointed to to 3
   delete bar; //deletes the int
   bar = 0; //removes the now invalid reference
}

指针本身就是在堆栈上创建的。 但是,bar指出的数据。 *酒吧 - 是的,星号在这里确实有所作为。 它可以获取指针指向的数据 - 在堆上。 当foo返回时,bar将超出范围。 如果我没有调用删除,我将失去对* bar的访问权限并且会出现内存泄漏。

对于第二个问题,dtor的作用是清理你构​​造的对象。 默认为您提供一个,但有时您需要编写自己的。 例如,如果您的类中包含原始指针,则需要在dtor中使用delete显式释放它们。 当对象超出堆栈内存的范围并且为堆内存调用delete时调用它。

  • 堆栈是在函数和方法调用中使用的区域。 它基本上用于存储参数和函数本地数据(以及调用函数的过程中涉及的其他内容)。 堆栈是一种LIFO类型的数据结构,这意味着您放入的最后一件事就是您从中获得的第一件事。

  • 另一方面,堆是一个免费存储,我的意思是你可以从堆中获取任何值,无论它放在那里的顺序 - 这种区别很重要。

  • 在C ++中,每次调用new时,都会在Heap上创建对象,并返回指向该对象的指针,然后您可以使用该指针访问对象。 这适用于复杂的对象,例如你或其他人创建的类中的对象,以及内置类型,如int,char,double等......使用new没有什么神奇之处,在它的核心只是调用malloc ,或者根据平台的一些风格,然后调用对象的构造函数。 您必须记住为使用new创建的每个对象调用delete (或delete []p_array_of_object_pointers )。

  • 析构函数delete my_object_pointer在代码中delete my_object_pointer时语言调用对象的函数。 析构函数使您有机会在代码中进行一些清理。 在C ++这样的语言中,你必须管理自己的资源,你通常会用它来调用你在堆上分配的其他对象上的deletefree ,或者关闭你用fopen创建的文件句柄等等......

  • 这两个关键字都在函数和方法的内部或外部工作,无论你从何处调用它们,它们都将始终与堆一起工作。 您在不使用new情况下创建的任何内容将最终出现在其他位置,可执行文件的静态区域或堆栈,具体取决于创建的方式和位置。

  • 每次使用以下语法MyClassType myObject(contructor, arguments);在方法或函数内创建对象时MyClassType myObject(contructor, arguments); 并且在不使用new ,您将在堆栈上创建它(除非您将其声明为静态,即它最终在我上面提到的静态区域中)。

希望这可以帮助。

堆分配使用new关键字完成

C ++没有垃圾收集,永远不会自动释放堆分配

您可以使用delete关键字释放它们

你需要保留一个指针,例如通过从函数返回它,或者将它指定给一个全局变量

析构函数在delete调用,用于释放资源,例如网络套接字

所以代码就是,例如return new MyObject("foo");

如何分配到堆:使用new关键字

如何从堆中释放:使用delete关键字。

为避免程序崩溃和内存泄漏,每个new必须具有指定对象和类型的delete副本。

无论对象何时被存储(堆,堆栈等),都会在删除对象时调用析构函数

这里有关于内存的更具体信息: 堆栈和堆的内容和位置是什么?

在以下时间使用动态分配的对象实例

1)在编译时不知道对象的实际类型2)当您想要创建的对象数量在编译时未知时3)当(编译器生成的)对象的生命周期管理不满足您的需求时。

使用new运算符创建的每个对象都应该在某个时候使用delete运算符释放。 如果不这样做,您将有内存泄漏,如果您依赖于对象析构函数的副作用,您将不会得到这些副作用,并且您的程序可能无法执行所需的操作。

当一个对象被创建为堆栈变量时,编译器将插入代码以在对象超出范围时调用对象析构函数。 编译器如何知道要调用的析构函数? 对象的类型在编译时是已知的,因此只有一个可能性,只有一个析构函数可以调用。

如果使用'delete'来销毁对象,编译器可能无法分辨使用哪个析构函数。 对象可以是'basic'类型,然后将调用析构函数'basic :: ~basic()'。

    class basic;
    class derived : public basic
    {
           ....
           ~derived();
    }

    basic *l = new derived();
    ...
    delete l;

编译器不知道对象的实际类型不是“基本”而是“派生”。 但是,如果将“基本”的析构函数声明为虚拟 ,则编译器将插入查询对象虚拟方法表的代码,并将调用重定向到正确的析构函数(在这种情况下,析构函数会破坏类型的对象)衍生')。

如果你担心这个问题,如果可以的话,让析构函数“虚拟”。

您可以在示例中在堆栈中分配内存。 它是自动变量,它将被删除然后返回发生。 如果使用new关键字,则必须提供正确的解除分配

MyObject* newObject = new MyObject(a);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM