Chapter 14 Smart Pointers¶
标准库中的智能指针
std::unique_ptr:独占所有权,默认首选std::shared_ptr:共享所有权,内部维护引用计数std::weak_ptr:不拥有对象,只用于观察shared_ptr管理的对象std::auto_ptr(C++11 起已废弃)
目标
- 介绍如何维护对象的引用计数
- 被共享对象由
UCObject保存计数 - 智能指针
UCPointer<T>负责在赋值、复制、析构时维护计数
1 Reference Counting¶
共享对象内部维护一个计数器,记录当前有多少个指针正在共享它。
Reference counts in action
String x("abcdef");
String y = x; // shallow copy
x = "Hello world"; // copy on write

x和y初始可共享同一份底层表示,引用计数为 2- 若其中一个被写入修改,则可分离成两份数据
每个可共享对象有一个计数器,初始值通常是 0,指针赋值时需要先对旧对象减计数,再对新对象加计数。
Have to do the following
p->decrement();
p = q;
p->increment();
UCObject:负责引用计数UCPointer<T>:智能指针模板String:对外暴露的接口类(Envelope)StringRep:真正持有字符数据的表示类(Letter)

2 Class UCObject¶
Reusing reference counting
#include <assert.h>
class UCObject {
public:
UCObject() : m_refCount(0) {}
virtual ~UCObject() { assert(m_refCount == 0); }
UCObject(const UCObject&) : m_refCount(0) {}
void incr() { m_refCount++; }
void decr();
int references() { return m_refCount; }
private:
int m_refCount;
};
decr()
inline void UCObject::decr() {
m_refCount -= 1;
if (m_refCount == 0) {
delete this;
}
}
delete this 只对堆对象安全,不要对栈对象这么做。
3 Class UCPointer¶
示例
template <class T>
class UCPointer {
private:
T* m_pObj;
void increment() { if (m_pObj) m_pObj->incr(); }
void decrement() { if (m_pObj) m_pObj->decr(); }
public:
UCPointer(T* r = 0) : m_pObj(r) { increment(); }
~UCPointer() { decrement(); }
UCPointer(const UCPointer<T>& p);
UCPointer& operator=(const UCPointer<T>&);
T* operator->() const;
T& operator*() const { return *m_pObj; }
};
拷贝构造
template <class T>
UCPointer<T>::UCPointer(const UCPointer<T>& p) {
m_pObj = p.m_pObj;
increment();
}
赋值运算
template <class T>
UCPointer<T>& UCPointer<T>::operator=(const UCPointer<T>& p) {
if (m_pObj != p.m_pObj) {
decrement();
m_pObj = p.m_pObj;
increment();
}
return *this;
}
4 Envelope / Letter Pattern¶
Envelope and Letter
- 信封(Envelope):提供对外的保护与接口
- 信件(Letter):承载实际的内容数据

String是外层信封,负责对外接口StringRep是内部信件,负责真实数据与实现细节StringRep继承UCObject,因此天然带引用计数String通过UCPointer<StringRep>持有共享表示
示例:String
class String {
public:
String(const char*);
~String();
String(const String&);
String& operator=(const String&);
int operator==(const String&) const;
String operator+(const String&) const;
int length() const;
operator const char*() const;
private:
UCPointer<StringRep> m_rep;
};
StringRep
class StringRep : public UCObject {
public:
StringRep(const char*);
~StringRep();
StringRep(const StringRep&);
int length() const { return strlen(m_pChars); }
int equal(const StringRep&) const;
private:
char* m_pChars;
};
StringRep Implementation
StringRep::StringRep(const char* s) {
if (s) {
int len = strlen(s) + 1;
m_pChars = new char[len];
strcpy(m_pChars, s);
} else {
m_pChars = new char[1];
*m_pChars = '\0';
}
}
StringRep::~StringRep() {
delete[] m_pChars;
}
深拷贝表示层
StringRep::StringRep(const StringRep& sr) {
int len = sr.length();
m_pChars = new char[len + 1];
strcpy(m_pChars, sr.m_pChars);
}
int StringRep::equal(const StringRep& sp) const {
return strcmp(m_pChars, sp.m_pChars) == 0;
}
String Implementation
String::String(const char* s)
: m_rep(new StringRep(s)) {}
String::~String() {}
String::String(const String& s)
: m_rep(s.m_rep) {}
String& String::operator=(const String& s) {
m_rep = s.m_rep;
return *this;
}
转发到表示层
int String::operator==(const String& s) const {
return m_rep->equal(*s.m_rep);
}
int String::length() const {
return m_rep->length();
}
外层 String 的拷贝构造和赋值几乎可以“交给智能指针处理”,这样对外接口仍保持值语义,但底层表示可以共享。
Critique
- 优点:
UCPointer统一维护引用计数UCObject隐藏计数细节StringRep只关心字符串数据本身UCObject和UCPointer可复用
- 缺点:
- 比裸指针慢
- 设计有侵入性:被管理对象必须继承
UCObject - 标准库中的
std::shared_ptr提供了非侵入式方案