众所周知,Qt窗口小部件使用隐式共享。那么我很想知道STL容器std::vector
、std::string
是否也使用隐式共享。
如果没有,为什么?因为它非常有用。
如果答案是肯定的,我们如何确定呢?我需要一个简单的C++ STL程序,它可以显示STL容器使用隐式共享。在复制时不进行深度复制。
不可以。当您尝试修改容器的内容,甚至调用它的可变 begin()
方法时,这可能会导致潜在的写时复制,从而使所有引用和迭代器失效。这将是一个难以调试的情况,因此被禁止。
尽管 std::string
在技术上不是容器,但自 C++11 起,仍禁止执行写时复制:
引用、指针和迭代器引用 basic_string 序列的元素可能会被以下使用 basic_string 对象的操作所使无效:
...
— 调用非 const 的成员函数,除了 operator[]、at、front、back、begin、rbegin、end 和 rend。
[string.require]
...因为它非常有用。
嘿,有何用处?几乎总是通过引用传递解决所有的“性能问题”。原子引用计数在多处理器机器上本质上是不可扩展的。
detach()
复制。我将不得不审查我所有答案中的代码,以确保我不会无意中让任何人暴露在这个问题下。 - Kuba hasn't forgotten Monica*i = 5
,它会指向新的副本吗? - Andystd::vector
或std::string
的operator[]
时尤其危险。
线程
人们可能会合理地期望能够复制构造一个容器,以便将其交给另一个线程处理,之后就不必再担心并发问题了。但是使用CoW就不是这样。正如在类似问题中所注意到的那样:
C++标准并不禁止或规定std::string的写时复制或任何其他实现细节。只要符合语义和复杂性要求,实现可以选择任何实现策略。
我认为,对于std::vector也是如此。
此外,您可能会对这个主题感兴趣:std::string实现的方式是什么?
s[0] = 'a'
不允许使指向 s
的指针、引用或迭代器失效,这意味着它在返回第一个 char
的引用之前不能进行复制。此外,将该段与 C++03 标准进行比较,后者规定了不同的规则,提到了首次调用非 const operator[]
、非 const at()
等(特别是为了允许 COW 实现在非 const 引用“泄漏”时进行深复制)。 - Jonathan WakelySTL容器不使用隐式共享。它们始终具有普通值语义。
原因是运行时性能:在多线程程序中(可能但不一定在多核主机上运行),管理数据的锁定开销(例如参考计数,在写入之前复制时锁定)远远超过了普通值复制的开销,后者根本没有特殊的线程含义。预计那些需要频繁复制大型std::maps
的程序将实现显式共享以避免复制。
事实上,在STL的早期阶段,std::string
确实使用隐式共享。但是当第一个多核CPU出现时,它被删除了。
string
的引用计数实现,因为它们在多线程环境下表现不佳。对于C++03我不确定。 - Andy Prowl