如何在堆栈和堆对象之间转换
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 &
并且你有一个从堆中得到的Class * ptr
,你可以简单地取消引用指针并将它传递给函数(例如func(*ptr)
),没有错用它。顺便说一句,一般来说,如果函数获得引用,对象生命周期管理仍然在调用方。
Konrad,您是说编译器实际上不会复制它,而是会移动堆栈中的实际数据?它仍然使深堆栈上的许多对象更有可能导致溢出的问题,但另一方面,在返回对象副本方面确实更有意义(我在某些地方广泛使用了 memcpy()处理大量数据的代码,我知道它的速度有多快)。我仍然需要堆对象来满足我的目的,但我以后会记住这一点。
Matteo,我已经试过了,不管你信不信,在某些系统上它不起作用。例如,使用void doThis(Class& 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】:我认为您真的想问“如何返回在我的函数中创建的对象?”有几种有效的方法:
在堆上分配并返回一个指针 使用自动变量并返回其值,而不是指针(编译器会复制它) 让调用者通过指针或引用参数提供存储,并在那里构建您的对象。【讨论】:
以上是关于如何在堆栈和堆对象之间转换的主要内容,如果未能解决你的问题,请参考以下文章