【问题标题】:Would this be considered good C++ code这会被认为是好的 C++ 代码吗
【发布时间】:2010-11-09 09:49:05
【问题描述】:

我有一个带有原始指针的向量(不,我不能使用智能指针),我想在 for 循环中将项目添加到列表中。我做了一个小试验项目,我想知道这在指针管理方面是否被认为是好的 C++ 代码。

请只考虑原始指针管理,对于我要解决的这个特定问题,我对智能指针不感兴趣。

一个简单的对象:

class Request
{
public:
    std::string name;

};

std::vector<Request*> requests;

for (int i = 0; i < 5; i++)
{
    std::stringstream ss;
    ss << "elemenent ";
    ss << i;

    std::string s = ss.str();

    Request* req = new Request();   
    req->name = s;

    requests.push_back(req);

}

编辑:

所以我要解决的问题是将 DOMNode* 添加到来自this 库的向量中。
我开始觉得尝试为我的项目需要的这个库中的部分编写一个包装器是一个坏主意。还是图书馆不好? 我还没有使用 smart_ptr 让它正常工作,如果有人有,那么我想听听。

【问题讨论】:

  • 如果您不使用智能指针,那么这完全取决于您打算如何清理这些请求。如果这在我们的审核中,我还建议使用构造函数来初始化请求名称。
  • 真的不清楚你在问什么。此代码中没有指针管理。您正在堆上创建一些对象,然后永远不要删除它们。
  • 真的很简单,我使用的库只使用指针,没有对象有复制构造函数。我需要将其中一些对象添加到列表中,我想知道正确的方法是什么。

标签: c++ list pointers


【解决方案1】:

嗯,这会泄漏内存,所以很糟糕。你能用Pointer Container吗?

此代码泄漏的原因是您使用new 在堆上创建对象,但您从未在它们上调用delete

至于你的评论,如果你有一个手动管理一些资源的对象,你需要The Big Three

【讨论】:

  • 太好了,我怀疑我在泄漏...如果请求只有一个私有副本 ctor 并包含指针成员怎么办?
  • +1 或智能指针容器(boost::shared_ptr 或 std::tr1::smart_ptr)。
  • 我知道我从来没有对它们调用 delete,但它们仍然在列表中,如果我在 for 循环结束时调用 delete,我在列表中的指针指向垃圾,不是吗?
  • @Tony:是的,但它容易出错并且需要不需要的代码。你真的应该试试boost::shared_ptr 它是为这个特定任务设计的。
  • @Tony:您只需要了解 Request 对象的生命周期。创建它们的 for 循环没有任何问题。在您使用完它们之后,但在向量本身超出范围之前,请确保您再次遍历向量并删除每个指针。就是这么简单。像“ur”这样的建议有助于通过绑定循环删除指向向量生命周期的指针来确保可靠地完成。
【解决方案2】:

我认为您在方法的末尾有一个循环,用于对 vector 的每个成员调用 delete。

仍然存在问题,特别是异常安全问题。

  • 如果在创建Request 和在vector 中注册之间出现任何问题,那么您已经失去了记忆。一种解决方案是暂时使用scoped_ptr 来保留内存,push_backptr.get() 然后调用release 方法,因为现在内存归vector 所有。
  • 如果在您创建 vector 中的项目和销毁它们之间有任何抛出,您需要捕获异常,销毁项目,然后重新抛出。

可能还有其他人,但发明 RAII 是有原因的,没有它真的很难做到(正确地......)

【讨论】:

    【解决方案3】:

    如果您不能使用智能指针,请使用boost::ptr_vector

    请注意,如果您使用TinyXmlXmlNode 中的内存管理可能由库决定 - 最近的历史 iirc 是您的许多问题与正确理解该库的内存所有权和释放范例有关。

    What memory management do I need to cleanup when using TinyXml for C++?

    What is the best open XML parser for C++?

    【讨论】:

      【解决方案4】:

      如果您不能(或不允许)使用智能指针,也许您可​​以使用这样的简单内存管理器:

      template <class T>
      class MemManager
      {
      public:
        typedef std::vector<T*> Vec;
        ~MemManager ()
        {
          size_t sz = v_.size ();
          for (size_t i = 0; i < sz; ++i)
            delete v_[i];
        }
        T* pushNewObject () 
        {
          T* t = NULL;
          try
          {
              t = new T;
              if (t != NULL)
                 v_.push_back(t);            
          }
          catch (std::bad_alloc& ex) { /* handle ex */ }
          return t;
        }
        const Vec& objects() const { return v_; }
      private:
        Vec v_;
      };
      
      // test
      {
        MemManager<Request> mm;
        for (int i = 0; i < 5; i++)
          {
            std::stringstream ss;
            ss << "elemenent ";
            ss << i;
      
            std::string s = ss.str();
      
            Request* req = mm.pushNewObject();
            req->name = s;    
          }
      } // all Request objects will be deleted here when 
        // the MemManager object goes out of scope.
      

      【讨论】:

      • 这也泄漏了。 (阅读三巨头并考虑异常安全)
      • @Matthieu 你能解释一下这是如何/在哪里泄漏的吗?
      • @Vijay:如果push_back 失败会怎样?诚然,它应该只是因为 std::bad_alloc 而发生,这意味着要么你的内存不足,要么只是因为 vector 增长太多并且可能不再找到足够大小的块。还有MemManager可复制的问题,你应该禁止复制和分配。最后,在界面说明上:newObject 的意义何在?我会说要么让用户处理内存(隐藏new 并没有提供太多),要么提供一个完整的包装器,它将同时分配和注册对象:)
      • @Matthieu 是的,你是对的。用一种可能的修复方法更新了答案。
      • @Matthieu 我的目的只是提出一个想法,而不是提供一个万无一失的解决方案,在 C++ 中,它需要非常小心并符合许多铁规则。
      【解决方案5】:

      一个快速的改进可能是从std::vector&lt;Request*&gt; 派生一个类RequestVector,添加一个ClearRequests 方法(删除所有Request 对象并清除向量)并使其成为析构函数调用ClearRequests。 (实际上聚合RequestVector 中的向量可能是更好的选择,但派生类完成得更快。

      【讨论】:

      • -1:Inheriting from STL-Containers is discouraged。您应该根据自己的建议使用组合。
      • 我喜欢从 STL 容器继承。如果您不传递指向其基类的指针,那是非常安全的,在 99.9% 的情况下您甚至不会考虑这样做。您多久存储一次指向容器的指针?有多少次你甚至会考虑使用指向基类的指针?你的容器多久在堆上?它们绝大多数都在堆栈或成员数据上。这是 FUD。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-03
      • 2016-02-16
      相关资源
      最近更新 更多