7

如果我们考虑使用引用计数的 std::string 实现,请考虑以下场景:

int main()
{
  string english = "Hello";
  string german  = english; //refcnt = 2
  string german2 = german;

  /* L1 */ german[1] = 'a';
  /* L2 */ *(german2.begin() + 1) = 'A';

  cout << english << endl << german << endl << german2 << endl;
  return 0;
}

L1 和 L2 会发生什么?引用计数是否中断并执行了深层复制?我是这么认为的,但我担心的是,如果发生这种情况,请执行以下简单操作:

cout << german[1] << endl; 

或者一个简单的:

cout << *(german.begin()) << endl;

在非常量上下文中会执行不必​​要的深度复制。我对吗?实现如何处理这个细节?

4

2 回答 2

6

你是对的,在所有四个示例(L1、L2 和下面的两个)中都会制作一个副本,即使对于后两个是不必要的。

不幸的是,当调用 operator[] 的非常量版本或取消引用非常量迭代器时,实现无法判断生成的非常量引用是否将用于修改对象,因此它必须安全行事并制作副本。

C++11 向字符串和其他容器添加了函数cbegin()cend()返回 const 迭代器,即使在非 const 对象上调用也是如此。这有助于缓解问题。我不知道 operator[] 的类似解决方案。

注意:正如其他一些回答者所建议的那样,让 operator[] 或迭代器的 operator*() 返回代理类型并不是真正的选择,因为它违反了容器要求,其中之一是这些函数返回实际引用。(这就是为什么现在每个人都同意这vector<bool>是一个错误的原因——它以这种方式使用代理)。

(当然,如果您正在编写自己的引用计数类,那么没有什么能阻止您使用代理类型来实现这一点。)

于 2012-06-21T23:59:23.043 回答
2

实现此目的的一种方法是通过代理类。因此,当您索引字符串而不是获取字符时,您会得到一个看起来和感觉都像字符的对象。当对其执行写入时,它会导致原始字符串的深层复制。

于 2012-06-21T23:59:19.510 回答