如果我们考虑使用引用计数的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;在非常数上下文中将执行不必要的深度复制。我说的对吗?实现是如何处理这个细节的?
发布于 2012-06-22 07:59:23
您是对的,在所有四个示例(L1、L2和下面的两个)中都会创建一个副本,尽管对于后两个示例是不必要的。
不幸的是,当非常量版本的operator[]被调用或者非常量迭代器被取消引用时,实现没有办法知道产生的非常量引用是否将用于修改对象,因此它必须确保它的安全并进行复制。
C++11将函数cbegin()和cend()添加到字符串和其他容器中,这些容器返回常量迭代器,即使在非常量对象上调用也是如此。这有助于缓解问题。我不知道operator[]有什么类似的解决方案。
注意:让operator[]或迭代器的运算符*()返回代理类型,正如其他一些回答者所建议的那样,并不是一个真正的选择,因为它违反了容器要求,其中之一是这些函数返回实际的引用。(这就是为什么现在每个人都认为vector<bool>是一个错误-它以这种方式使用代理)。
(当然,如果您正在编写自己的引用计数类,那么没有什么可以阻止您使用代理类型来实现这一点。)
发布于 2012-06-22 07:59:20
实现这一点的一种方法是通过代理类。因此,当您索引到一个字符串时,如果获取一个char,您将得到一个看起来和感觉上都像char的对象。当对它执行写操作时,它会导致对原始字符串进行深度复制。
https://stackoverflow.com/questions/11148355
复制相似问题