简体   繁体   English

是使用std :: vector <T*> 不如std :: vector安全 <shared_ptr<T> &gt;?

[英]Is use of std::vector<T*> less safe than std::vector<shared_ptr<T>>?

Say I have a simple data class: 说我有一个简单的数据类:

struct Foo
{
   int iData;
   double dData;
};

I can create a container class that could be implemented as: 我可以创建一个可以实现为的容器类:

Version 1 版本1

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

or as: 或为:

Version 2 版本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;
};

Granted that Version 1 is a lot simpler and easier to maintain than Version 2 . 可以肯定的是, 版本1版本2更容易维护。 However, I have couple of questions that are orthogonal to that aspect of the code. 但是,我有几个与代码的这一方面正交的问题。

  1. Is Version 1 safer than Version 2 from the point of view of dealing with exceptions? 从处理异常的角度来看, 版本1是否比版本2安全?
  2. If so, how? 如果是这样,怎么办?

Yes, the first option is more exception safe. 是的,第一种选择更安全。 Namely, your copy constructor has two memory leak sources, the copy assignment operator has a memory leak and is simply plain wrong. 即,您的副本构造函数有两个内存泄漏源,副本赋值运算符有内存泄漏,这完全是错误的。 Additionally, the first option has move assignment and a move constructor by default, giving improved performance. 此外,默认情况下,第一个选项具有移动分配和移动构造函数,从而提高了性能。 (Even more performance if you used std::unique_ptr instead) (如果改用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;
}

Version 1 and version 2 have different semantics. 版本1和版本2具有不同的语义。 If you copy a Bar object, version one gives shared access to the data, while version 2 does not. 如果复制Bar对象,则版本1提供对数据的共享访问,而版本2不提供对数据的共享访问。 Version 2 also has a subtle memory leak; 第2版​​也有微妙的内存泄漏。 if new succeeds while push_back fails, the object leaks. 如果new成功而push_back失败,则对象泄漏。

Why not use the even simpler std::vector<Foo> ? 为什么不使用更简单的std::vector<Foo> I'm not seeing any rationale for using pointers (either shared or raw) in this case. 我没有看到在这种情况下使用指针(共享指针或原始指针)的任何理由。 Value semantics are much, much easier to reason about than pointer semantics. 值语义比指针语义容易得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM