若你需要的只是分发指向同一个对象的多个指针,并且当最后一个指针消失的时候自动释放该对象的能力的话,你可以使用以下的类似“智能指针”的类。
代码如下:
// Fred.h
class FredPtr;
class Fred {
public:
Fred() : count_(0) { } // 所有的构造函数都要设置count_为0!
private:
friend FredPtr; // 友元类
unsigned count_;
// count_必须被所有构造函数初始化
// count_就是指向this的FredPtr对象数目
};
class FredPtr {
public:
Fred* operator-> () { return p_; }
Fred& operator* () { return *p_; }
FredPtr(Fred* p) : p_(p) { ++p_->count; } // p不能为NULL
~FredPtr() { if (--p_->count_ == 0) delete p_; }
FredPtr(const FredPtr& p) : p_(p.p_) { ++p_->count_; }
FredPtr& operator= (const FredPtr& p) {
// 不要改变这些语句的顺序
// 如此的顺序适当地处理了(自赋值)
++p.p_->count;
if (--p_->count_ == 0) deleete p_;
p_ = p.p_;
return *this;
}
private:
Fred* p_; // p_永远不为NULL
};
当然,你也可以使用嵌套类,将FredPtr改为Fred::Ptr
引用计数可以由指针计数或引用计数完成,上方显示了如何使用指针语义进行引用计数,下方将展示如何使用引用语义进行引用计数。
基本思想是允许用户认为他们在复制Fred对象,而实际上真正的实现并不真正复制,直到一些用户试图修改隐含的Fred对象时才复制。
Fred::Data类装载了Fred类所有的数据。Fred::Data也有一个额外的成员count_,来管理引用计数。Fred类最后成了一个指向Fred::Data的“智能指针”(内部的)
class Fred {
public:
Fred(); // 默认构造函数
Fred(int i, int j); // 普通构造函数
Fred(const Fred& f); // 拷贝构造函数
~Fred();
void sampleInspectorMethod() const; // this对象不会变
void sampleMutatorMethod(); // 会改变this()对象
//...
private:
class Data {
public:
Data();
Data(int i, int j);
Data(const Data& d);
// 由于只有Fred能访问Fred::Data对象
// 只要你愿意,你可以使得Fred::Data的数据为public
// 但如果那样使你不爽,就把数据作为private
// 还要用friend Fred,使Fred成为友元类
unsigned count_;
// count_是指向this的Fred对象的数目
// count_必须被所有的构造函数初始化为1
// (从1开始是因为它被创建它的Fred对象所指)
};
Data* data_;
Fred::Data::Data() : count_(1) {}
Fred::Data::Data(int i, int j) : count_(1) {}
Fred::Data::Data(const Data& d) : count_(1) {}
Fred::Fred() : data_(new Data()) {}
Fred::Fred(int i, int j) : data_(new Data(i, j)) {}
Fred::Fred(const Fred& f)
: data_(f.data_) {
++data_->count_;
}
Fred& Fred::operator= (const Fred& f) {
// 不要更改这些语句的顺序
// 如此的顺序恰当的处理了自赋值问题
++f.data_->count_;
if (--data_->count_ == 0) delete data_;
data_ = f.data_;
return *this;
}
Fred::~Fred() {
if (--data_->count_ == 0) delete data_;
}
void Fred::sampleInspectorMethod() const {
// 该方法承诺"const",不改变*data_中任何东西
// 除此之外,任何数据访问将简单地使用data_->..
}
void Fred::sampleMutatorMethod() {
// 该方法可能需要改变*data_中的数据
// 因此首先检查this是否唯一指向*data_
if (data_->count_ > 1) {
Data* d = new Data(*data);
-- data_->count_;
data_ = d;
}
assert(data_->count_ == 1);
// 现在改方法如常进行"data_->..."访问
}
};
如果非常经常地调用 Fred 的默认构造函数,你可以为所有通过Fred::Fred()构造的Fred 共享一个公共的Fred::Data 对象来消除那些 new调用。为避免静态初始化顺序问题,该共享的 Fred::Data 对象在一个函数内“首次使用”时才创建。如下就是对以上的代码做的改变(注意,该共享的Fred::Data对象的析构函数永远不会被调用;如果这成问题的话,要么解决静态初始化顺序的问题,要么索性返回到如上描述的方法):
// 如果非常经常地调用 Fred 的默认构造函数,
// 你可以为所有通过Fred::Fred()构造的Fred 共享一个公共的Fred::Data 对象来消除那些 new调用。
// 为避免静态初始化顺序问题,该共享的 Fred::Data 对象在一个函数内“首次使用”时才创建。
// 如下就是对以上的代码做的改变(注意,该共享的Fred::Data对象的析构函数永远不会被调用;
// 如果这成问题的话,要么解决静态初始化顺序的问题,要么索性返回到如上描述的方法):
class Fred {
public:
//..
private:
//..
static Data* defaultData();
};
Fred::Fred()
: data_(defaultData()) {
++ data_->count_;
}
Fred::Data* Fred::defaultData() {
static Data*p = NULL;
if (p == NULL) {
p = new Data();
++ p->count_; // 确保它不为0
}
return p;
}
// 前一个FAQ给出了引用语义的引用计数策略,
// 但迄今为止都针对单个类而不是分层次的类。
// 本FAQ扩展之前的技术以允许为类层次提供引用计数。
// 基本不同之处在于现在Fred::Data是类层次的根,着可能使得它有一些虚函数。
// 注意 Fred 类本身仍然没有任何的虚函数。
// 虚构造函数用法用来建立 Fred::Data 对象的拷贝。要选择创建哪个派生类,
// 如下的示例代码使用了命名构造函数用法,但还有其它技术(构造函数中加一个switch语句等)。
// 示例代码假设了两个派生类:Der1和Der2。派生类的方法并不查觉引用计数。
class Fred {
public:
static Fred create1(const std::string& s, int i);
static Fred create2(float x, float y);
Fred(const Fred& f);
Fred& operator= (const Fred& f);
~Fred();
void sampleInspectorMethod() const; // this对象不会被改变
void sampleMutatorMethod(); // 会改变this对象
private:
class Data {
public:
Data() : count_(1) {}
Data(const Data& d) : count_(1) {} // 不要拷贝'count_'成员
Data& operator= (const Data&) { return *this; } // 不要拷贝'count_'成员
virtual ~Data() { assert(count_== 0); } // 虚析构函数
virtual Data* clone() const = 0; // 虚构造函数
virtual void sampleInspectorMethod() const = 0; // 纯虚函数
virtual void sampleMutatorMethod() = 0;
private:
unsigned count_; // count_不需要是protected的
friend Fred; // 允许Fred访问count_
};
class Der1 : public Data {
public:
Der1(const std::string& s, int i);
virtual void sampleInspectorMethod() const;
virtual void sampleMutatorMethod();
virtual Data* clone() const;
};
class Der2 : public Data {
public:
Der2(float x, float y);
virtual void sampleInspectorMethod() const;
virtual void sampleMutatorMethod();
virtual Data* clone() const;
};
Fred(Data* data);
// 创建一个拥有*data的Fred智能引用
// 它是private的以迫使用户使用createXXX()方法
// 要求:data必能为NULL
Data* data_; // invariant: data_ is never NULL
};
Fred::Fred(Data* data) : data_(data) { assert(data != NULL); }
Fred Fred::create1(const std::string& s, int i) { return Fred(new Der1(s, i)); }
Fred Fred::create2(float x, float y) { return Fred(new Der2(x, y)); }
Fred::Data* Fred::Der1::clone() const { return new Der1(*this); }
Fred::Data* Fred::Der2::clone() const { return new Der2(*this); }
Fred::Fred(const Fred& f)
: data_(f.data_) {
++ data_->count_;
}
Fred& Fred::operator= (const Fred& f) {
// 不要改变这些语句的顺序!
// 如此的顺序适当地处理了自赋值问题
++ f.data_->count_;
if (--data->count_ == 0) delete data_;
data_ = f.data_;
return *this;
}
Fred::~Fred() {
if (--data_->count_ == 0) delete data_;
}
void Fred::sampleInspectorMethod() const {
// 该方法承诺"const", 不改变*data_中的任何东西
// 因此我们只要直接把方法传递给*data_
data_->sampleInspectorMethod();
}
void Fred::sampleMutatorMethod() {
// 该方法可能需要改变*data_中的数据
// 因此首先检查this是否唯一指向*data_
if (data_->count_ > 1) {
Data* d = data_->clone(); // 虚构造函数用法
-- data_->count_;
data_ = d;
}
assert(data_->count_ == 1);
// 现在直接把方法传递给*data_;
data_->sampleInspectorMethod();
}
// 自然,Fred::Der1和Fred::Der2的构造函数和sampleXXX方法将需要被以某周途径适当的实现