5

例子:

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”。很简单。现在,如果我们运行它,我们会得到:

Dealloc
Buff:你好!
增益:¯ø_ˇ

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

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

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

4

7 回答 7

9

所以,第一个问题是:如果我如何将堆栈对象(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);
}

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

于 2011-02-20T18:03:00.550 回答
5

所以,第一个问题是:如果我如何将堆栈对象(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<string>* 。如果动态分配 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 的定义。

那么正确的方法是什么?

  1. 了解为什么应该使用堆栈对象
  2. 了解什么是智能指针。
  3. 了解如何使用智能指针来控制对象的生命周期。
  4. 了解不同类型的智能指针。
  5. 查看关注点分离是什么(你没有遵循这个基本原则)。
于 2011-02-20T18:11:40.973 回答
1

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

于 2011-02-20T18:00:51.107 回答
0

唯一的方法是复制对象。

将声明更改为:

Class _obj2;

并分配:

_obj2 = obj2;

于 2011-02-20T17:59:01.403 回答
0

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

您可以更改Class* obj2Class obj2并分配(这意味着复制)对象obj2 = obj2;

于 2011-02-20T18:00:46.440 回答
0

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

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

于 2011-02-20T18:02:55.560 回答
0

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

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