【问题标题】:Creating an object on the stack then passing by reference to another method in C++在堆栈上创建一个对象,然后通过引用传递给 C++ 中的另一个方法
【发布时间】:2011-04-15 02:51:55
【问题描述】:

我从 C# 背景转向 C++。假设我有一个方法可以在堆栈上的方法中创建一个对象,然后我将它传递给另一个类方法,该方法将它添加到一个成员向量中。

void DoStuff()
{
    SimpleObj so = SimpleObj("Data", 4);
    memobj.Add(so); 
}

//In memobj
void Add(SimpleObj& so)
{
   memVec.push_back(so); //boost::ptr_vector object
}

这是我的问题:

  1. DoStuff 方法结束后,so 是否会超出范围并从堆栈中弹出?
  2. memVec 有一个指向 so 的指针,但它被弹出了这里发生了什么?
  3. 将堆栈对象传递给将它们存储为指针的方法的正确方法是什么?

我意识到这些对于有一定经验的 C++ 程序员来说可能是显而易见的。

标记

【问题讨论】:

  • 作为旁注,“SimpleObj so = SimpleObj("Data", 4);"不是很 c++ish,使用 "SimpleObj so("Data", 4);"
  • 谢谢,还有一些 C# 宿醉。;
  • @milan1612:不幸的是,它仍然是最便携的符号。一堆 c++ 编译器在后者上失败了,similarly as in the case.

标签: c++ gcc boost memory-management


【解决方案1】:

要实现您的目标,您应该使用shared_ptr

void DoStuff()
{
    boost::shared_ptr<SimpleObj> so(new SimpleObj("Data", 4));
    memobj.Add(so); 
}

//In memobj
void Add(boost::shared_ptr<SimpleObj> so)
{
   memVec.push_back(so); // std::vector<boost::shared_ptr<SimpleObj> > memVec;
}

【讨论】:

  • 好吧,这是有道理的,所以一旦添加到向量中,ptr 上的引用计数将为 1,然后它不会被销毁。
  • @Mark:是的。并且当容器被销毁时,容器中每个shared_ptr的引用计数都会减一(如果达到0,对象将被当场删除)。
  • 如果您使用 C++0x,我会推荐 unique_ptr 而不是 shared_ptr - 开销更少。它没有制作增加引用计数的副本,而是四处移动。
  • 您必须小心使用unique_ptr 以避免意外移出容器,留下一个 NULL 指针。除非您真的知道自己在做什么,否则请使用 shared_ptr
【解决方案2】:
  1. 是的。
  2. 指针保持“活动”状态,但指向一个不再存在的对象。这意味着当您第一次尝试取消引用此类指针时,您将陷入未定义的行为(您的程序可能会崩溃,或者更糟糕的是,会继续运行并给出“奇怪”的结果)。
  3. 如果您想在函数返回后保留它们,您根本不这样做。这就是使用堆分配和存储副本对象的容器的原因。

实现您想要做的最简单的方法是将对象的副本存储在普通的 STL 容器中(例如std::vector)。如果这些对象是重量级的并且复制成本很高,您可能希望在堆上分配它们并将它们存储在足够智能指针的容器中,例如boost::shared_ptr(参见@Space_C0wb0y's answer 中的示例)。

另一种可能性是将boost::ptr_vectorboost::ptr_vector_owner 结合使用;最后一个类负责“拥有”存储在关联ptr_vector 中的对象,并在超出范围时删除所有指针。有关ptr_vectorptr_vector_owner 的更多信息,您可能需要查看this article

【讨论】:

  • 谢谢,我想避免复制对象,但也避免必须管理堆上的内存,所以我想我会看看最后两个选项。
  • 由于你来自 C#,我建议你也看看 boost 提供的其他智能指针,并了解它们每个的更好用处。 C++ 中的内存管理不像 C# 中那么容易,但是使用正确的智能指针,它几乎是自动的。顺便说一句,如果我的回答或其他回答解决了您的问题,您可以考虑将其标记为“已接受”。
  • 是的,我正在使用 Boost 库和智能指针,只是没有完全理解它们,但您启发了我,谢谢。
  • 在 C++0x 中我推荐std::vector&lt;std::unique_ptr&lt;T&gt;&gt;,它是一个异常安全、低开销、自动内存管理的容器,它可以满足你的所有需求,并且比 boost:: ptr_vector(IIRC 不适用于某些 STL 算法)。
  • 是的,新的移动语义使得编写在多种情况下工作得更好的智能指针成为可能(尤其是在 STL 容器中)。如果我还有票,我会支持你的评论。 :)
【解决方案3】:

为了存储指向对象的指针,必须使用 new 创建它。否则会在超出范围时消失。
这里提出的问题最简单的解决方案是使用 std::vector 而不是 boost::ptr_vector,因为这样 push_back 会复制向量中的对象

【讨论】:

    【解决方案4】:

    是的,一旦您的函数离开作用域,您的 so 对象就会从堆栈中弹出。您应该使用new 创建一个堆对象,并在向量中添加一个指向该对象的指针。

    如前所述,一旦您的第一个函数超出范围,向量中的指针将指向未定义的内容

    【讨论】:

      【解决方案5】:

      该代码无法编译,因为在 Add 函数中您试图将整个对象推入一个需要指向对象的指针的向量中。

      如果您取而代之的是获取该对象的地址并将其推送到向量上,那么这将是危险的,因为原始对象很快就会从堆栈中弹出,并且您存储的指针将指向未初始化的内存。

      如果您使用的是法线向量而不是指针向量,那么 push_back 调用将复制整个对象而不是指针,因此它是安全的。但是,这不一定有效,而且“复制所有内容”的方法对于 C#、Java 或 Python 领域的人来说可能并不直观。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-12
        • 2015-09-15
        相关资源
        最近更新 更多