我读到的内容是,制作拥有MyObject
指针的指针向量的通用方法是vector<unique_pointer<MyObject>>
。
但是,每次访问元素时,都会调用unique_ptr::get()
。还有一点开销。
为什么没有带有“ custom deleter”的指针向量,如果存在这样的东西(我没有使用分配器),则更标准吗?即,智能向量而不是智能指针的向量。它将消除使用unique_ptr::get()
的小开销。
类似vector<MyObject*, delete_on_destroy_allocator<MyObject>>
或unique_vector<MyObject>
的东西。
向量将采用行为“销毁时删除指针”,而不是在每个unique_ptr中重复此行为,这是有原因的,还是开销可以忽略不计?
答案 0 :(得分:1)
存储数据的首选方法不是unique_ptr
的向量,也不是普通指针的向量。在您的示例中:std::vector<MyObject>
通常就很好,如果您知道编译时的大小,请尝试std::array<int>
。
如果您绝对需要间接引用,也可以考虑使用std::vector<std::reference_wrapper<MyObject>>
。了解参考包装here。
已经说过……如果您
MyObject
的移动非常大/昂贵,或者MyObject
的构造或破坏具有现实的副作用; 另外,您希望当MyObject
不再从向量中引用时被释放-唯一指针的向量是相关的。
现在,指针只是从C语言继承而来的简单数据类型;它没有自定义删除器或任何自定义内容...,但是-std::unique_ptr
does support custom deleters。同样,可能是由于您有更复杂的资源管理需求,而让每个元素管理自己的分配和取消分配没有意义-在这种情况下,“智能”向量类可能是相关的。 / p>
因此:不同的数据结构适合不同的情况。
答案 1 :(得分:1)
如果存在这样的东西,为什么没有带有“ custom deleter”的指针向量
因为这样的事情不存在并且不能存在。
提供给容器的分配器用于为容器分配内存,并(可选)在该容器中创建/销毁对象。 vector<T*>
是指针的容器;因此,分配器为指针分配内存,并(可选)创建/销毁指针。它不负责指针的 content :它指向的对象。那是用户提供和管理的领域。
如果分配器负责销毁所指向的对象,那么从逻辑上讲它还必须负责创建所指向的对象,是吗?毕竟,如果没有这样做,并且我们复制了这样的vector<T*, owning_allocator>
,则每个副本都会破坏指向的对象。但是由于它们指向相同的对象(复制vector<T>
会复制T
s),因此会造成双重破坏。
因此,如果owning_allocator::destruct
要删除内存,owning_allocator::construct
必须还将创建所指向的对象。
所以...这是怎么做的?
vector<T*, owning_allocator> vec;
vec.push_back(new T());
看到问题了吗? allocator::construct
无法决定何时创建T
,何时不创建。它不知道是由于vector
复制操作还是由于用户创建的push_back
调用了T*
而调用了它。它所知道的只是用T*
值调用(从技术上讲是对T*
的引用,但这是不相关的,因为在两种情况下都将用这样的引用来调用它。)
因此,它要么1)分配一个新对象(通过给定的指针从副本中初始化),要么2)复制指针值。而且由于无法检测到正在发生什么情况,因此必须始终选择相同的选项。如果它执行#1,则上面的代码将导致内存泄漏,因为该向量未存储new T()
,并且没有其他人将其删除。如果它执行的是#2,则您将无法复制这样的向量(内部向量重新分配的故事也同样模糊)。
无法获得想要的东西。
vector<T>
是T
的容器,无论T
是什么。它将T
视之为原;此值的任何含义取决于用户。所有权语义就是其中的一部分。
T*
没有所有权语义,因此vector<T*>
也没有所有权语义。 unique_ptr<T>
具有所有权语义,因此vector<unique_ptr<T>>
也具有所有权语义。
这就是为什么Boost具有ptr_vector<T>
的原因,而T
是明确地的向量风格的类,专门包含指向T*
的指针。因此,它的界面略有修改。如果您将其交给T*
,则表明它正在采用T
并将销毁它。如果您将其交给T
,则它将分配一个新的T
并将值复制/移动到新分配的vector<T*>
中。这是一个不同的容器,具有不同的接口和不同的行为。因此,它应具有与{{1}}不同的 type 。