在STL中使用引用计数的数据结构有哪些行为异常?

时间:2012-08-30 13:08:59

标签: c++ data-structures stl garbage-collection containers

“有效STL”中的Scott Meyer说,在决定使用哪种数据结构时要考虑的一件事是容器是否使用引用计数。他说这种方法存在一些行为异常。

它们中有哪些是什么?为什么像'string'和'rope'这样的容器有异常行为?

4 个答案:

答案 0 :(得分:3)

正如其他人所说,典型的例子是std::string。除了锁定多线程程序的性能问题外,引用计数字符串还存在无线程问题。想象一下:

string s = "hello";
string t = s;                   // s and t share data
char &c = t[0];                 // copy made here, since t is non-const

问题是非const operator[]必须复制字符串(如果它已共享),因为稍后可以使用返回的引用来修改字符串(您可以将其指定给非引用{ {1}},但char并不知道它的行为应该有所不同。另一方面, const operator[]应该避免复制,因为这会消除引用计数的所有好处(这意味着你总是复制一个实践)。

operator[]

正如您所看到的,这种区别令人困惑,可能会导致意外行为。

这是一篇关于写时复制语义及其对性能影响的旧文章:http://www.gotw.ca/gotw/045.htm

以下是有动机将const char &get_first(const string &s) { return s[0]; // no copy, s is const } string s = "hello"; string t = s; // s and t share data const char &c1 = get_first(t); // no copy made here const char &c2 = t[0]; // copy made, since t is non-const // c1 just got invalidated (in fact, it's pointing at s[0], not t[0]). s[0] = 'X'; printf("%c, %c\n", c1, c2); // outputs "X, h" 更改为在C ++ 11标准中不被引用计数的提案:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2534.html。这就是上面的例子所基于的。

答案 1 :(得分:1)

作为一个示例,一个可能发生在引用计数字符串上的异常,特别是带有“子部分”处理(带有开始/结束切片)的字符串,是“不幸的锁定”。

让我们假设您为文件的整个文本分配内存。然后解析文件并使用一些“slice()”,“left()”,“mid()”或等效方法。您可能会结束锁定文件的整个字符串,而可能只有一小部分包含实际的文本数据(剩余部分已经解析过数字,标点符号或其他内容)。因此,您可能最终使用了更多内存,同时更容易控制峰值使用。在这种情况下,如果您使用多线程并在各种线程中密集使用某些字符串,可能会出现第二个问题:不必要的内存争用,字符串的引用计数可能会增加/减少所有时间和原子性可能会阻碍所有线程的运行。

但是,只要您知道应用程序中的潜在问题并阻止它们(在这种情况下只是通过复制它们来“单独”使字符串),就没有任何反对引用计数的内容。

答案 2 :(得分:1)

作为一般规则,引用计数会受到竞争条件,死锁或过度同步等常见多线程问题的影响。

然后你会遇到通常需要类似闭包行为的上下文问题,即在显然超出范围之后可能需要捕获对象,但这可以通过STL避免,我不是STL专家。

这里有一个讨论,讨论与智能指针相关的各种巴洛克边缘案例:http://www.velocityreviews.com/forums/t689414-c-primer-4th-edition-reference-counting-smart-pointers.html

答案 3 :(得分:1)

在第13项中,Meyers详细阐述了多线程和refcounted字符串的问题。

这在很大程度上取决于std::string的确切实现以及锁定和使用模式。
如果在多线程环境中对std::string的明显无害的使用导致由于隐藏锁定而导致延迟,则可能成为问题。循环中这种锁和上下文切换的代价可能很大。但它永远不应该导致死锁。
它不一定是个问题。这本书大于10岁。在此期间,线程实现已得到改进。 Linux-Futexes,例如在大多数情况下表现得更顺畅。

另一点:(由我,dunno,如果迈耶斯也讨论过这个......) refcounted std::string表示它具有写时复制语义。这通常是一件好事。实际副本将推迟到实际需要时为止。但这也意味着副本的价格必须在可能难以预测的地方支付。