如何在堆栈和堆对象之间转换

Posted

技术标签:

【中文标题】如何在堆栈和堆对象之间转换【英文标题】:How to Convert beween Stack and Heap Objects 【发布时间】:2011-06-30 20:07:26 【问题描述】:

例子:

Class *_obj1;
Class *_obj2;

void doThis(Class *obj) 

void create() 
    Class *obj1 = new Class();
    Class obj2;

    doThis(obj1);
    doThis(&obj2);

    _obj1 = obj1;
    _obj2 = &obj2;


int main (int argc, const char * argv[]) 

    create();

    _obj1->doSomething();
    _obj2->doSomething();

    return 0;

这会创建 2 个对象,创建指向它们的指针,然后 main() 对每个对象调用一个方法。 Class 对象创建一个 char* 并存储 C 字符串“Hello!”在里面; ~Class() 释放器释放内存。 doSomething() 方法使用 printf() 打印出“buff: %s”。很简单。现在,如果我们运行它,我们会得到:

释放 爱好者:你好! 增益:¯ø_ˇ

显然堆栈对象在这里不起作用 - 很明显,当函数退出时,指针 _obj2 指向堆栈中的某个位置。这就是我在上一个问题中使用堆对象的原因,人们告诉我这很“愚蠢”。

所以,第一个问题是:如果 我如何将堆栈对象 (obj2) 转换为堆对象,以便在 create() 退出后不会释放它? 我想要一个直接的答案,而不是傲慢的“你做错了”,就像许多人所做的那样。因为在这种情况下堆栈对象无法工作,所以堆对象似乎是唯一的方法。编辑:另外,转换回堆栈对象也很有用。

第二个问题:堆对象“错误”的具体示例是使用new 运算符创建一个新的vector<string>*如果动态分配 STL 对象是错误的,那么正确的方法是什么? 显然,如果您将它们创建为堆栈对象,它会失败,因为它们会立即被释放,但我被告知(再次,非常高级成员)动态分配它们可能会破坏堆。那么正确的做法是什么?

【问题讨论】:

您的另一个问题已关闭,因为 充满了无意义的咆哮。如果您想提出一个真正的问题,请将您的旧问题编辑为符合主题并且可以接受,而不是提出新问题。如果这是一个真正的问题,而不仅仅是一个诱饵,那么“获得一本好的 C++ 书籍”是您可以期待的最佳质量答案。这是一个复杂的主题,这个平台上的答案无法做到公正。 如果您需要帮助。别再粗鲁了,好好问问题。 如果人们告诉你“你显然需要读一本 C++ 书”,这可能有道理吗? 这两种说法没有矛盾。有问题的代码不会导致堆损坏(没有人这么说),但是虚假地使用堆分配会大大增加错误的风险,进而导致堆损坏。除此之外,sbi 对“愚蠢”一词的使用是……嗯,愚蠢,因为缺乏知识并不等同于愚蠢。在他的辩护中,你认真地引诱他们,为什么当有人上钩时你会感到惊讶? @Konrad:说句公道话,我在解释和总结我的内容时,恰好一次使用了“stupid”这个词,in a comment和其他人说过。在my original answer 我说过这个“几乎肯定是错的” 【参考方案1】:

所以,第一个问题是:如果我如何将堆栈对象 (obj2) 转换为堆对象,以便在 create() 退出后它不会被释放?我想要一个直接的答案,

直接的答案是:您不能在堆栈和堆之间“转换”对象。正如其他人指出的那样,您可以创建位于另一个空间中的对象的副本,但仅此而已。

第二个问题:堆对象“错误”的具体示例是使用 new 运算符创建新向量*。如果动态分配 STL 对象是错误的,那么正确的方法是什么?显然,如果您将它们创建为堆栈对象,它会失败,因为它们会立即被释放,但我被告知(再次,一位非常高级的成员)动态分配它们会破坏堆。

动态分配 STL 对象本身不会破坏堆。 (不知道你可能在哪里听说过。)

如果您想在创建它的函数之外使用堆栈分配的 STL 对象,您不能,因为该对象所在的堆栈空间仅在创建它的函数。

但是,您可以返回对象的副本:

std::vector<char> SomeFunc()

    std::vector<char> myvector;
    // myvector.operations ...
    return myvector;

不过,正如我所说,这将返回对象的副本,而不是原始对象本身——这是不可能的,因为包含该对象的堆栈在函数返回后展开。

如果这对您的特定场景有意义,则另一种选择是让调用者传入指向您的函数操作的对象的引用/指针:

void SomeFunc(std::vector<char>& destination)

    // destination.operations ...


void AnotherFunc()

    std::vector<char> myvector;
    SomeFunc(myvector);

如您所见,您仍然在堆栈上分配了所有内容,并且避免了依赖复制构造函数返回对象副本的(有时是必然的)开销。

【讨论】:

返回对象的副本比这个答案听起来要便宜得多,因为编译器几乎总是可以删除副本。 感谢您终于回答了实际问题。我确实知道堆栈/堆对象(这就是为什么我用这个例子来说明为什么堆栈对象不起作用)但是我找不到一种简单的方法来有效地在它们之间进行转换(没有复制构造函数)并使用堆期望堆栈对象的对象(即使用 &variable 返回指向函数范围内的堆栈对象的指针,但我从来没有能够让它以相反的方式工作,例如当函数需要 Class& 和你想给它一个堆对象)。如果有办法我会很高兴听到它... 如果一个函数需要一个Class &amp;并且你有一个从堆中得到的Class * ptr,你可以简单地取消引用指针并将它传递给函数(例如func(*ptr)),没有错用它。顺便说一句,一般来说,如果函数获得引用,对象生命周期管理仍然在调用方。 Konrad,您是说编译器实际上不会复制它,而是会移动堆栈中的实际数据?它仍然使深堆栈上的许多对象更有可能导致溢出的问题,但另一方面,在返回对象副本方面确实更有意义(我在某些地方广泛使用了 memcpy()处理大量数据的代码,我知道它的速度有多快)。我仍然需要堆对象来满足我的目的,但我以后会记住这一点。 Matteo,我已经试过了,不管你信不信,在某些系统上它不起作用。例如,使用void doThis(Class&amp; obj) ,如果我有Class *obj1 = new Class();,然后执行doThis(*obj1);,它可以在某些系统上运行,但在其他系统上不可用(即我们正在处理的一些特定的Solaris 机器,以及一些Linux 变体)。我不确定它是否只是 GCC 的版本,或者该特定用法是否是依赖于实现的功能,但我已经学会不依赖它,因为它在我处理的某些系统上不起作用。 【参考方案2】:

所以,第一个问题是:如何将堆栈对象 (obj2) 转换为堆对象,以便在 create() 退出后不会释放它?

这一行:

_obj2 = &obj2;

改为:

_obj2 = new Class(obj2);  // Create an object on the heap invoking the copy constructor.

我想要一个直截了当的答案,而不是像许多人那样傲慢地“你做错了”。

这是你能得到的最直接的答案。显然你是 C++ 的新手,所以我相信这不会按预期工作,因为你可能在定义类“Class”时犯了几个错误(顺便说一句可怕的名字)。

此外,转换回堆栈对象也很有用。

class obj3(*_obj2);  // dereference the heap object pass it to the copy constructor.

第二个问题:堆对象“错误”的具体例子是使用 new 运算符创建一个新的 vector*。如果动态分配 STL 对象是错误的,那么正确的做法是什么?

为什么要动态分配向量。只需在本地创建即可。

std::vector<std::string> funct()

    std::vector<std::string>   vecString;
    // fill your vector here.

    return vecString;  // Notice no dynamic allocation with new,

使用 new/delete 就像使用 C 一样使用 C++。您需要阅读的是智能指针。这些对象控制对象的生命周期,并在超出范围时自动删除对象。

std::auto_ptr<Class>   x(new Class);

这里的 x 是一个智能指针(类型为 auto_ptr),当它超出范围时,对象将被删除。但是您可以将 auto_ptr 返回给调用函数,它将安全地转移出函数。它实际上比这要复杂得多,你需要一本书。

显然,如果您将它们创建为堆栈对象,它会失败,因为它们会立即被释放,

当它超出范围时,它会被取消分配。

但我被告知(再次,一位非常高级的成员)动态分配它们会破坏堆。

如果你做错了。鉴于您的知识,这很有可能。但很难验证,因为您没有提供 Class 的定义。

那么正确的做法是什么?

    了解为什么应该使用堆栈对象 了解什么是智能指针。 了解如何使用智能指针来控制对象的生命周期。 了解不同类型的智能指针。 看看什么是关注点分离(你没有遵循这个基本原则)。

【讨论】:

【参考方案3】:

您必须复制构造一个新的堆对象 (Class * foo = new Class(obj2)) 或将堆栈对象分配给一个堆对象 (*obj1 = obj2)。

【讨论】:

是的,虽然我会推荐使用复制构造方法而不是赋值。另外,如果你做赋值方法,你首先要创建一个新的类对象,所以它需要两个步骤:_obj2 = new Class(); *_obj2 = obj2; - 所以你可以看到为什么我更喜欢一步复制构造函数。【参考方案4】:

唯一的方法是复制对象。

将声明更改为:

Class _obj2;

并分配:

_obj2 = obj2;

【讨论】:

【参考方案5】:

获取堆栈变量的地址不会神奇地将其转移到堆中。您需要为您的班级编写一个适当的复制构造函数并使用_obj2 = new Class(obj2);

至于 STL 容器,它们无论如何都在堆上分配数据,为什么要在堆上分配容器本身呢?将它们放在一个范围内,只要您需要它们,它们就会一直保持活力。

【讨论】:

【参考方案6】:

您的堆栈对象是在 create 函数内创建的,一旦超出该函数的范围就会被释放。指针无效。

您可以将Class* obj2 更改为Class obj2 并通过obj2 = obj2; 分配(意味着复制)对象

【讨论】:

堆对象在被释放之前保持有效。由于此程序从不删除它,因此它将一直有效,直到程序结束。 @Ben:我认为 dwo 的意思可能是“堆栈”而不是“堆”,因此我进行了相应的编辑。【参考方案7】:

我认为您真的想问“如何返回在我的函数中创建的对象?”有几种有效的方法:

在堆上分配并返回一个指针 使用自动变量并返回其值,而不是指针(编译器会复制它) 让调用者通过指针或引用参数提供存储,并在那里构建您的对象。

【讨论】:

以上是关于如何在堆栈和堆对象之间转换的主要内容,如果未能解决你的问题,请参考以下文章

堆栈内存和堆内存之间的区别[重复]

堆栈和堆如何工作,内存存储(重复)[重复]

堆栈地址和堆地址之间的关系?

现代计算机的堆栈和堆空间

程序栈和堆,它们是如何工作的?

C++ 中的 RAM、硬盘、堆栈和堆是啥?