更多 C++ 惯用法/写时复制
外观
实现延迟复制优化。就像延迟初始化一样,只在需要的时候才执行操作,以提高效率。
- COW (写时复制)
- 延迟复制
复制对象有时会导致性能下降。如果对象经常被复制,但在以后很少被修改,写时复制可以提供显著的优化。为了实现写时复制,一个指向实际内容的智能指针用于封装对象的价值,并且在每次修改时,都会检查对象的引用计数;如果对象被引用多次,则会在修改之前创建内容的副本。
#ifndef COWPTR_HPP
#define COWPTR_HPP
#include <memory>
template <class T>
class CowPtr
{
public:
typedef std::shared_ptr<T> RefPtr;
private:
RefPtr m_sp;
void detach()
{
T* tmp = m_sp.get();
if( !( tmp == 0 || m_sp.unique() ) ) {
m_sp = RefPtr( new T( *tmp ) );
}
}
public:
CowPtr(T* t)
: m_sp(t)
{}
CowPtr(const RefPtr& refptr)
: m_sp(refptr)
{}
const T& operator*() const
{
return *m_sp;
}
T& operator*()
{
detach();
return *m_sp;
}
const T* operator->() const
{
return m_sp.operator->();
}
T* operator->()
{
detach();
return m_sp.operator->();
}
};
#endif
这种写时复制的实现是通用的,但除了必须通过智能指针解除引用来引用内部对象的不便之外,它至少有一个缺点:那些返回对其内部状态引用的类,比如
char & String::operator[](int)
可能会导致意想不到的行为。[1]
考虑以下代码片段
CowPtr<String> s1 = "Hello";
char &c = s1->operator[](4); // Non-const detachment does nothing here
CowPtr<String> s2(s1); // Lazy-copy, shared state
c = '!'; // Uh-oh
最后一行代码的目的是修改原始字符串s1
,而不是副本,但作为副作用,s2
也意外被修改了。
一个更好的方法是编写一个自定义的写时复制实现,该实现封装在我们想要延迟复制的类中,对用户来说是透明的。为了解决上述问题,可以将那些已经将内部状态的引用传递出去的对象标记为“不可共享”——换句话说,强制复制操作深度复制对象。作为优化,可以在任何不传递内部状态引用(例如,void string::clear()
)的非常量操作之后将对象恢复为“可共享”,因为客户端代码期望这些引用会被无效化。[1]
- 活动模板库
- 许多 Qt 类 (隐式共享)