考虑下面的例子,我有一个必须通过智能指针在免费商店中分配的对象,例如,我可以依赖客户从工厂获取所述对象.此对象包含一些使用STL容器专门分配的数据,该容器恰好是std :: vector.在一种情况下,这个数据向量本身是使用一些智能指针动态分配的,而在另一种情况下我只是不使用智能指针.
设计A和设计B之间是否存在实际差异,如下所述?
情况A:
class SomeClass{ public: SomeClass(){ /* initialize some potentially big STL container */ } private: std::vector<double> dataVector_; };
情况B:
class SomeOtherClass{ public: SomeOtherClass() { /* initialize some potentially big STL container,but is it allocated in any different way? */ } private: std::unique_ptr<std::vector<double>> pDataVector_; };
一些工厂功能.
std::unique_ptr<SomeClass> someClassFactory(){ return std::make_unique<SomeClass>(); } std::unique_ptr<SomeOtherClass> someOtherClassFactory(){ return std::make_unique<SomeOtherClass>(); }
使用案例:
int main(){ //in my case I can reliably assume that objects themselves //are going to always be allocated in dynamic memory auto pSomeClassObject(someClassFactory()); auto pSomeOtherClassObject(someOtherClassFactory()); return 0; }
我希望两种设计选择都有相同的结果,但它们呢?
选择A或B有任何优势或劣势吗?具体来说,我一般应该选择设计A,因为它更简单或有更多考虑因素吗? B在道德上是错误的,因为它可以为std :: vector悬挂吗?
tl; dr:智能指针指向STL容器是不是错了?
编辑:
相关答案指出了对我这样困惑的人有用的额外信息.
Usage of objects or pointers to objects as class members and memory allocation
和Class members that are objects – Pointers or not? C++
并且更改一些谷歌关键字会将我带到When vectors are allocated,do they use memory on the heap or the stack?
解决方法
你正在添加一个间接,但更重要的是,SomeClass的全部内容在访问内容时变成3个独立的内存块(SomeClass与/包含指向std :: vector的块指向其元素数组的unique_ptr的块).此外,您还需要支付额外多余的堆开销.
现在你可能开始想象一个间接对向量有帮助的场景,比如你可以在两个SomeClass实例之间浅移/交换unique_ptrs.是的,但是矢量已经提供了没有unique_ptr包装器的顶部.它已经有像空的状态,你可以重用一些有效/无效的概念.
请记住,可变大小的容器本身是小对象,而不是大对象,指向潜在的大块.矢量不大,其动态内容可以.为大对象添加间接的想法并不是一个坏的经验法则,但矢量不是一个大的对象.随着移动语义的到位,值得一提的是它更像是一个指向一个可以被浅层复制和廉价交换的大内存块.在移动语义之前,有更多的理由认为像std :: vector这样的东西是一个不可分割的大对象(尽管它的内容总是可以交换),但是现在值得把它想象成一个指向大而动态内容的小手柄.
通过unique_ptr之类的东西引入间接的一些常见原因是:
>抽象&躲了起来.如果你试图抽象或隐藏某些类型/子类型Foo的具体定义,那么这就是你需要间接的地方,这样它的句柄就可以被那些不知道的人捕获(或者甚至可能用于抽象)正是Foo是什么.
>允许一个大的,连续的1块类型对象从所有者传递给所有者,而不调用副本或使引用/指针(包括迭代器)无效到它或其内容.
>一种仓促的理由是浪费但有时候在最后期限内有用,就是简单地将有效性/无效状态引入一些本身并不存在的东西.
>偶尔,它可以作为一种优化来提升某些不常访问的,较大的对象成员,以便其常用的元素更适合(可能还有相邻的对象)在缓存行中. unique_ptr可以让你拆分该对象的内存布局,同时仍然符合RAII.
现在将shared_ptr包装在标准容器之上可能会有更多合法的应用程序,如果您有一个容器实际上可以由多个所有者(合理地)拥有.使用unique_ptr,一次只有一个所有者可以拥有该对象,标准容器已经允许您交换并移动彼此的内部内部(大的动态部分).所以我没有理由用直接用unique_ptr包装一个标准容器,因为它已经有点像一个动态数组的智能指针(但有更多的功能来处理动态数据,包括深层复制它,如果需要) .
如果我们谈论非标准容器,比如说你正在使用提供一些数据结构的第三方库,这些数据结构的内容可能会变得非常大但是它们无法提供那些廉价,无效的移动/交换语义,那么你可能表面上将它包装在unique_ptr周围,交换一些创建/访问/破坏开销,以便将这些廉价的移动/交换语义作为一种解决方法.对于标准容器,不需要这样的解决方法.