在C++引用计数原理以及简易版引用技数String的实现中,实现了一个简单的引用计数的String,并且也实现了operator []方法。
下面考虑这样一种情景:
RcString s1 = "hello";
char* p = &s1[1];
RcString s2 = s1;
*p = 'x';
上述代码 *p = 'x'同时修改了s1和s2, 而意图是仅仅修改s1, 那么如何解决这种问题呢?
首先我们分析:为什么*p = 'x' 会导致s1和s2同时被修改呢?
由RcString的operator[] 重载的方法可知,s1[1]调用了non-const operator[] 而该方法返回的是同一个Stringvalue的内容的引用。因此会产生共享对象的修改被所有拥有者所见。
那么解决上述问题呢?
问题既然出现在operator[],因此我们的着手点便也在该函数。当我们调用non-cosnt []函数时,便使共享值不在被其拥有者共享,然后拷贝一个副本至被修改的拥有者,便可以解决这个问题。为此,需要为每个Stringvalue对象增加一个标志,表示该对象是否被共享。只要non-const operator[]被调用便清除该标记,该标记一旦被清除便永远不会恢复。
因此新的RcString定义如下:
class RcString {
public:
RcString(const char* value = "");
RcString(const RcString& rhs);
~RcString();
RcString& operator=(const RcString& rhs);
const char& operator[](int index) const {
return _data->_data_ref[index];
}
char& operator[](int index);
char* get() {
return _data->_data_ref;
}
private:
struct StringValue {
StringValue(const char* value);
~StringValue();
char* _data_ref;
int _refcount;
bool _shareable;
};
StringValue* _data;
};
RcString::StringValue::StringValue(const char *value) :
_refcount(1), _shareable(true) {
_data_ref = new char[strlen(value) + 1];
strcpy(_data_ref, value);
}
此时对于RcString的拷贝构造函数,需要将共享标志纳入考虑范围:其实现修正如下:
RcString::RcString(const RcString &rhs) {
if (_data->_shareable) {
_data = rhs._data;
++_data->_refcount;
} else {
_data = new StringValue(rhs._data->_data_ref);
}
}
同理,修改的non-const operator[]方法如下:
char & RcString::operator[](int index) {
if (_data->_refcount > 1) {
--_data->_refcount;
_data = new StringValue(_data->_data_ref);
}
_data->_shareable = false;
return _data->_data_ref[index];
}