在std :: vector中存储具有std :: auto_ptr作为其成员变量的类的对象是否安全?

时间:2009-03-31 08:35:15

标签: c++ memory-management stl

我不能在我的项目中使用shared_ptr,没有提升:(

所以,我的课程与下面的课程大致类似:

class MyClass
{
private:
  std::auto_ptr<MyOtherClass> obj;
};

现在,我想在std :: vector中存储上面类的实例。安全吗?我read here将std :: auto_ptr与STL容器一起使用是错误的。这适用于我的情况吗?

8 个答案:

答案 0 :(得分:6)

这是不安全的,因为当容器将复制MyClass时,instnace默认复制操作符将为所有成员调用copy,并且对于auto_ptr成员也是如此,我们将遇到您在问题中描述的相同情况(将auto_ptr存储在容器中)

BTW:为避免在编译时混淆,请添加

private:
  MyClass& operator=( const MyClass& );  
  MyClass( const MyClass& );
如果您尝试使用复制操作符,

编译器输出错误,这可以帮助您节省数小时的调试时间。

答案 1 :(得分:3)

正如尼尔巴特沃斯所说,auto_ptr可能不是可行的方法。

boost::shared_ptr显然是,但你说你不能使用助推器。

让我提一下,您可以下载boost,仅使用bcp tool提取共享\ ptr所需的内容并使用boost::shared_ptr。它只意味着你的项目中添加了一些hpp文件。我相信这是正确的方式。

答案 2 :(得分:3)

在标准容器中包含auto_ptr的对象有效。您遇到了未定义的行为。两个常见问题:

  • std :: vector&lt;&gt; :: resize将其参数复制到每个创建的元素中。第一个副本将“成功”(请参阅​​下面的原因),但每个进一步的副本都将为空,因为复制的元素也是空的!
  • 如果在重新分配期间抛出某些内容,您可能会将某些元素复制(到新缓冲区) - 但是副本被丢弃 - 而其他元素则没有,因为push_back如果异常则不会产生任何影响被扔了。因此,你的一些元素现在是空的。

因为这完全是关于未定义的行为所以它并不重要。但即使如果我们试图根据我们认为有效的行为来提出这种行为,我们无论如何都会失败。所有成员函数如push_backresize等都有一个const引用,它接受类型为T的对象。因此,类型T const&的引用被尝试复制到元素中。向量。但隐式创建的复制构造函数/复制赋值运算符看起来像T(T&) - 也就是说,它需要复制非const 对象!标准库的良好实现检查,并在必要时无法编译。

直到下一个C ++版本,你必须忍受这个。下一个将支持仅可移动的元素类型。也就是说,移动的对象不需要等于移动到的对象。这将允许将所有权转移指针线程放入容器中。

查看标准对此所说的内容(17.4.3.6):

  

在某些情况下(替换函数,处理函数,用于实例化标准库模板组件的类型的操作),C ++标准库依赖于C ++程序提供的组件。如果这些组件不符合要求,则标准对实施没有要求。

     

特别是,在以下情况下效果未定义:

     
      
  • 用于实例化模板组件时用作模板参数的类型,如果该类型的操作未实现适用的Requirements子句的语义(20.1.5,23.1,24.1,26.1)。
  •   

答案 3 :(得分:2)

  

我发布了一个问题作为后续跟进   对于这个答案,请参阅   Class containing auto_ptr stored in vector

假设你的类没有用户定义的拷贝构造函数,那么不,它可能(见下文)不安全。复制类时(将其添加到向量时会发生)将使用auto_ptr的复制构造函数。这有一种奇怪的行为,即将所复制的东西的所有权转移到副本中,因此被复制的东西现在为空。

虽然不太可能,但您确实需要此行为,在这种情况下auto_ptr是安全的。假设你没有,你应该:

  • 添加复制构造函数以管理复制

请注意这还不够 - 请参阅上面提到的后续问题以获取更多信息。

或:

  • 使用更智能的,可能引用计数的指针,例如其中一个提升智能指针

答案 4 :(得分:1)

复制MyClass对象将导致调用赋值运算符或复制构造函数。如果它们没有超载以处理auto_ptr&lt;&gt;以不寻常的方式,它们会将对复制构造函数(或赋值运算符)的调用传播到auto_ptr&lt;&gt;会员。这可能会导致您所链接的问题。

答案 5 :(得分:1)

实例化auto_pointer向量不安全的原因是有一个算法:sort(),它将在堆栈中的容器中复制一个对象。 (sort()实现了需要“pivot”的quicksort) 因此在退出sort()函数的scpope时删除它。 同样,任何能够将容器作为参数的算法或函数,以及将其中一个对象复制到堆栈上都会导致此问题。

在您的情况下,很简单,您必须确保您的类不像auto_ptr那样运行,或者确保您永远不会调用可以删除基础对象的函数/算法。根据我的说法,第一种解决方案是最好的:)

所以你的复制构造函数和你的做作运算符也不应该放弃指针对象的属性。

实现这一目标的最佳方法是使用boost智能指针代替auto_ptr,以便在调用此类函数/算法时使容器安全。

顺便说一句,根据我的说法,定义一个更好的复制构造函数/影响操作符来绕过这个问题并不是一个好的解决方案:我看不到一个好的复制构造函数实现(以及影响操作符),它可以保证安全应用sort()算法的结果。

答案 6 :(得分:1)

如果要在容器中使用auto_ptr的类,可以自己提供一个copy-constructor和赋值运算符:

class MyClass
{
private:
  const std::auto_ptr<MyOtherClass> obj; // Note const here to keep the pointer from being modified.

public:
  MyClass(const MyClass &other) : obj(new MyOtherClass(*other.obj)) {}
  MyClass &operator=(const MyClass &other)
  {
      *obj = *other.obj;
      return *this;
  }
};

但正如其他地方所述,该标准允许容器进行复制和分配,并假设所包含的类将以auto_ptr违反的特定方式运行。通过定义上面的方法,您可以创建一个包含auto_ptr行为的类。即使您的实现与auto_ptrs一起正常工作,您也有可能发现另一个实现不起作用。该标准仅保证性能和可观察行为,而不是实现。

答案 7 :(得分:0)

它无效。 auto_ptr不计算引用,这意味着在第一个析构函数调用时,您的指针将被释放。

请改用boost :: shared_ptr。