假设我有一个班级A
,我需要vector
个类A
的对象。
使用std::vector<A*>
或std::vector<A>
会更好吗?
我遇到一些讲座,提到前者不需要复制构造函数和复制赋值运算符的定义;而后者需要两者的定义。这是对的吗?
答案 0 :(得分:2)
讲义不完全正确:使用A
的向量使用复制构造函数/复制赋值运算符,但如果编译器提供的默认实现适合您,您无需提供自己的定义。
定义复制构造函数,赋值运算符和析构函数的决定与将对象放在容器中的决定无关。当您的类手动分配其资源时,您可以定义these three。否则,默认实现应该有效。
回到主要问题,存储指针与对象的决定主要取决于集合的语义,以及存储具有多态行为的对象的需要。
如果不需要多态行为,并且创建对象的副本相对便宜,使用vector<A>
是更好的选择,因为它允许容器为您管理资源。
如果复制很昂贵,并且您需要多态行为,则需要使用指针。但它们不一定需要是原始指针:C ++标准库提供了智能指针,可以为您处理清理,因此您不必手动销毁对象。
答案 1 :(得分:2)
与许多其他现代语言不同,C ++在价值类型上蓬勃发展。
如果存储指针,则必须管理生成的对象生命周期。除非您使用unique_ptr
或类似的智能指针,否则该语言不会对您有所帮助。丢失对象的轨迹正在泄漏:在处理它们之后跟踪它们是悬挂的参考/指针。两者都是非常常见的错误。
如果存储值(或类似值的类型),并教导数据如何有效地移动自身,vector
会将其连续存储在内存中。
在现代计算机上,CPU速度很快,内存速度非常慢。一台典型的计算机将具有3级缓存以便尝试更快地创建内存,但如果您的数据分散在整个内存中(因为您使用免费存储来存储对象),则CPU几乎没有机会找出您的位置打算进入下一个。
如果你的数据在一个连续的缓冲区中,不仅一个从内存中获取的内容会获得多个对象,而且CPU将能够猜测你将需要 next 块缓冲区中的内存,并为您预先获取它。
所以简短的版本是,如果您的对象大小适中,请使用对象的实际副本向量。如果它们适度地变大,则将经常访问的内容粘贴到对象中,并将对象内vector
中访问频率较大的部分放在一起,并编写有效的move
语义。然后将对象本身存储在vector
。
这有一些例外。
首先,值类型中的多态性很难,所以你最终会使用免费商店。
其次,一些对象最终将其位置作为其身份的一部分。矢量移动物体周围,以及&#34;复兴的成本&#34;一个对象可能不值得打扰。
第三,通常表现并不重要。所以你做的很容易。与此同时,价值类型并不那么难,虽然过早优化是一个坏主意,但过早的去优化也是如此。学习如何使用值类型和连续向量非常重要。
最后,学习零规则。零规则是管理资源的对象应该仔细编写其复制/移动/赋值/移动赋值/析构函数以遵循值语义规则(或阻止)。然后,使用这些资源管理对象的对象通常不需要实际写入其移动/复制/赋值/移动分配/析构函数 - 它们可以保留为空,=default
或类似。
你写的代码往往比你写的代码有更少的错误。
答案 2 :(得分:0)
您需要检查身份证明吗?如是 然后使用unique_ptr来存储它们,因为那时你不需要在以后删除它们。
是的,您需要复制操作,因为如果将A存储到矢量中,矢量会复制该对象。 使用A *,向量只能复制地址(指针)
答案 3 :(得分:0)
这是正确的,取决于。
在容器中存储指针可以提供额外的灵活性,因为您不需要这些操作符,或者您可以使这些操作符具有副作用和/或高成本。更糟糕的是,容器本身将执行指针的复制,其成本非常低。此外,您可以存储不同大小的对象(同一继承层次结构中的不同类的实例,尤其是如果它们具有虚拟方法)。
另一方面,存储对象本身会降低访问开销,因为每次访问元素时都不需要取消引用指针。此外,它将改善数据局部性(从而降低缓存未命中,页面未命中......)并减少内存消耗和碎片。
那里没有一般的经验法则。在构造函数和复制操作符中没有副作用的小对象可能更好地直接在经常读取且很少修改的容器中,而具有构造函数和具有昂贵副作用的复制操作符的大对象可能更好地适合经常修改的容器,调整大小...
您必须考虑您的使用案例并权衡每种方法的优缺点。