是否使用std :: vector <t *>比std :: vector <shared_ptr <t>&gt;?</shared_ptr <t> </t *>更安全

时间:2014-08-22 22:26:50

标签: c++ exception c++11

假设我有一个简单的数据类:

struct Foo
{
   int iData;
   double dData;
};

我可以创建一个可以实现为的容器类:

版本1

struct Bar
{
   std::vector<std::shared_ptr<Foo>> fooData;
};

或作为:

版本2

struct Bar
{
   Bar(){}

   Bar(Bar const& copy)
   {
      for(auto fooPtr: copy.fooData)
      {
         fooData.push_back(new Foo(*fooPtr));
      }
   }

   ~Bar()
   {
      for(auto fooPtr: fooData)
      {
         delete fooPtr;
      }
   }

   Bar& operator=(Bar const& rhs)
   {
      if ( this == &rhs )
      {
         return *this;
      }

      for(auto fooPtr: fooData)
      {
         delete fooPtr;
      }
      fooPtr.clear();  // Had missed this in the original code.

      for(auto fooPtr: rhs.fooData)
      {
         fooData.push_back(new Foo(*fooPtr));
      }

      return *this;
   }

   std::vector<Foo*> fooData;
};

承认版本1 版本2 更简单,更易于维护。 但是,我有几个与代码方面正交的问题。

  1. 从处理异常的角度来看版本1 版本2 更安全吗?
  2. 如果是这样,怎么样?

2 个答案:

答案 0 :(得分:4)

是的,第一个选项更安全。也就是说,您的复制构造函数有两个内存泄漏源,复制赋值运算符有内存泄漏,简直就是错误。此外,第一个选项默认具有移动赋值和移动构造函数,从而提高了性能。 (如果您使用std::unique_ptr代替,则表现更佳)

Bar(Bar const& copy)
{
   for(auto fooPtr: copy.fooData)
   {
      fooData.push_back(new Foo(*fooPtr));
      //what happens if `new Foo` throws a bad_alloc?
      //or if `Foo(const Foo&)` changes and now throws something?
      //or if `fooData.push_back` throws a bad_alloc?
      //anything you 'new'd is leaked.
   }
}

Bar& operator=(Bar const& rhs)
{
   if ( this == &rhs )
      return *this;

   for(auto fooPtr: fooData)
      delete fooPtr;
   //You forgot this line:
   //fooData.clear();

   for(auto fooPtr: rhs.fooData) //sharth notes you wrote copy.fooData but that's obvious
      fooData.push_back(new Foo(*fooPtr));
      //what happens if `fooData.push_back` throws a bad_alloc?
      //the `Foo` you just created is leaked.

   return *this;
}

答案 1 :(得分:1)

版本1和版本2具有不同的语义。如果复制Bar对象,则版本1提供对数据的共享访问,而版本2则不提供。版本2也有一个微妙的内存泄漏;如果new成功而push_back失败,则对象会泄漏。

为什么不使用更简单的std::vector<Foo>?在这种情况下,我没有看到使用指针(共享或原始)的任何理由。值语义比指针语义更容易推理。