在多线程环境中(现在大多数情况下),CoW 通常对性能造成巨大影响,而不是获得收益。并且仔细使用 const 引用,即使在单线程环境中也不会带来太大的性能提升。
这篇旧 DDJ 文章解释了just how bad CoW can be in a multithreaded environment, even if there's only one thread。
另外,正如其他人所指出的,CoW 字符串实现起来确实很棘手,而且很容易出错。再加上它们在线程情况下的糟糕表现让我真的怀疑它们的总体用途。一旦您开始使用 C++11 移动构造和移动赋值,这将变得更加真实。
但是,回答你的问题....
这里有一些可能有助于提高性能的实现技术。
首先,将长度存储在字符串本身中。长度被非常频繁地访问,并且消除指针取消引用可能会有所帮助。我会,只是为了保持一致性,把分配的长度也放在那里。就你的字符串对象而言,这会让你付出更大的代价,但空间和复制时间的开销非常小,特别是因为这些值将变得更容易让编译器发挥有趣的优化技巧。
这会给你一个字符串类,如下所示:
class MyString {
...
private:
class Buf {
...
private:
::std::size_t refct_;
char *data_;
};
::std::size_t len_;
::std::size_t alloclen_;
Buf *data_;
};
现在,您可以执行进一步的优化。那里的 Buf 类看起来并没有真正包含或做很多事情,这是真的。此外,它需要同时分配一个 Buf 实例和一个缓冲区来保存字符。这似乎相当浪费。因此,我们将转向一种常见的 C 实现技术,即弹性缓冲区:
class MyString {
...
private:
struct Buf {
::std::size_t refct_;
char data_[1];
};
void resizeBufTo(::std::size_t newsize);
void dereferenceBuf();
::std::size_t len_;
::std::size_t alloclen_;
Buf *data_;
};
void MyString::resizeBufTo(::std::size_t newsize)
{
assert((data_ == 0) || (data_->refct_ == 1));
if (newsize != 0) {
// Yes, I'm using C's allocation functions on purpose.
// C++'s new is a poor match for stretchy buffers.
Buf *newbuf = ::std::realloc(data_, sizeof(*newbuf) + (newsize - 1));
if (newbuf == 0) {
throw ::std::bad_alloc();
} else {
data_ = newbuf_;
}
} else { // newsize is 0
if (data_ != 0) {
::std::free(data_);
data_ = 0;
}
}
alloclen_ = newsize;
}
当您这样做时,您可以将 data_->data_ 视为包含 alloclen_ 字节,而不仅仅是 1。
请记住,在所有这些情况下,您都必须确保永远不会在多线程环境中使用它,或者确保 refct_ 是一种您同时拥有原子的类型增量,以及原子减量和测试指令。
还有一种更高级的优化技术,它涉及使用联合将短字符串存储在用于描述较长字符串的数据位中。但这更复杂,我不认为我会倾向于编辑它以便稍后在此处放置一个简化的示例,但你永远无法判断。