在类中存储指针或对象?

che*_*fin 5 c++ pointers class object move-semantics

只是一个设计/优化问题.什么时候存储指针或对象?为什么?例如,我相信这两个工作(除了编译错误):

class A{
  std::unique_ptr<Object> object_ptr;
};

A::A():object_ptr(new Object()){}

class B{
  Object object;
};

B::B():object(Object()){}
Run Code Online (Sandbox Code Playgroud)

我相信在堆栈或堆上实例化时会出现一个区别?

例如:

   int main(){
      std::unique_ptr<A> a_ptr;
      std::unique_ptr<B> b_ptr;
      a_ptr = new A(); //(*object_ptr) on heap, (*a_ptr) on heap?
      b_ptr = new B(); //(*object_ptr) on heap, object on heap?

      A a;   //a on stack, (*object_ptr) on heap?
      B b;   //b on stack, object on stack?
}
Run Code Online (Sandbox Code Playgroud)

sizeof(A)应该< sizeof(B)?我还有其他问题吗?(丹尼尔在评论中提醒我关于他的相关帖子中的继承问题)

因此,由于堆栈分配通常比堆分配快,但A的大小比B小,这些权衡中的哪一个如果没有在有问题的情况下测试性能,即使使用移动语义也无法回答?或者一些拇指规则当使用一个比另一个更有利时?(San Jacinto纠正了我关于堆栈/堆分配更快,而不是堆栈/堆)

我猜想更多的复制构建会导致相同的性能问题,(3个副本将比启动第一个实例的性能大约3倍).但是移动构造可能更有利于尽可能多地使用堆栈???

这是一个相关的问题,但不完全相同. C++ STL:我应该存储整个对象,还是指向对象的指针?

谢谢!

Ton*_*ion 6

如果你的内部有一个大对象A class,那么我会存储一个指向它的指针,但对于小对象或原始类型,在大多数情况下你不应该真正需要存储指针.

此外,当某些东西存储在堆栈上或堆上时(freestore)实际上是依赖于实现的,并且A a并不总是保证在堆栈上.

最好将其称为自动对象,因为它的存储持续时间由声明它的函数的范围决定.当函数返回时,a将被销毁.

指针需要使用new并且它确实带来了一些开销,但是在今天的机器上,我认为在大多数情况下它是微不足道的,除非你已经开始使用new数百万个对象,然后你将开始看到性能问题.

每种情况都不同,当你应该而且不应该使用指针而不是自动对象时,很大程度上取决于你的情况.

  • 以避免*过早优化*的方式相同,也应避免过早的优化*.如果没有充分的理由使用指针就行了.如果对类的成员使用动态分配,最终会产生大量的分配和释放(昂贵),内存碎片以及其他问题(例如对象中缺少数据的局部性). ..) (2认同)

San*_*nto 5

堆不比堆栈"慢".堆分配可能比堆栈分配慢,如果您以不存在大量连续内存访问的方式设计对象和数据结构,则较差的缓存局部性可能会导致大量缓存未命中.因此,从这个角度来看,这取决于您的设计和代码使用目标.

即使把它放在一边,你也必须质疑你的复制语义.如果你想要对象的深层副本(并且你的对象的对象也被深深复制),那么为什么甚至存储指针呢?如果由于复制语义而有共享内存是可以的,那么存储指针但确保你没有在dtor中释放两次内存.

我倾向于在两种情况下使用指针:类成员初始化顺序很重要,我将依赖注入到对象中.在大多数其他情况下,我使用非指针类型.

编辑:当我使用指针时还有两种情况:1)避免循环包含依赖项(虽然我可能在某些情况下使用引用),2)意图使用多态函数调用.


Ker*_* SB 5

这取决于许多具体因素,任何一种方法都有其优点.我会说如果你通过动态分配专门使用外部对象,那么你也可以让所有成员直接成员并避免额外的成员分配.另一方面,如果外部对象是自动分配的,那么大型成员应该通过a来处理unique_ptr.

通过指针处理成员还有一个额外的好处:删除编译时依赖项,外部类的头文件可以通过内部类的前向声明来逃避,而不是要求完全包含内部类class的标题("PIMPL").在大型项目中,这种脱钩可能在经济上是合理的.