拷贝构造函数和operatpr=对指针所指向内容作了一个新的拷贝
不止一个对象使用这个Dog,修改自己的Dog时,也等于修改他人的Dog
为了解决别名问题, 经常使用另外一个称为写拷贝 copy-on-write的技术
在向这块存储单元写之前,应该确信没有其他人使用它
如果引用记数大于1,在写之前必须拷贝这块存储单元,这样就不会影响他人了
简单的引用计数和写拷贝
//: C12:ReferenceCounting.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
// Reference count, copy-on-write
#include "../require.h"
#include <string>
#include <iostream>
using namespace std;
class Dog {
string nm;
int refcount;
Dog(const string& name)
: nm(name), refcount(1) {
cout << "Creating Dog: " << *this << endl;
}
// Prevent assignment:
Dog& operator=(const Dog& rv);
public:
// Dogs can only be created on the heap:
static Dog* make(const string& name) {
return new Dog(name);
}
Dog(const Dog& d)
: nm(d.nm + " copy"), refcount(1) {
cout << "Dog copy-constructor: "
<< *this << endl;
}
~Dog() {
cout << "Deleting Dog: " << *this << endl;
}
void attach() {
++refcount;
cout << "Attached Dog: " << *this << endl;
}
void detach() {
require(refcount != 0);
cout << "Detaching Dog: " << *this << endl;
// Destroy object if no one is using it:
if(--refcount == 0) delete this;
}
// Conditionally copy this Dog.
// Call before modifying the Dog, assign
// resulting pointer to your Dog*.
Dog* unalias() {
cout << "Unaliasing Dog: " << *this << endl;
// Don't duplicate if not aliased:
if(refcount == 1) return this;
--refcount;
// Use copy-constructor to duplicate:
return new Dog(*this);
}
void rename(const string& newName) {
nm = newName;
cout << "Dog renamed to: " << *this << endl;
}
friend ostream&
operator<<(ostream& os, const Dog& d) {
return os << "[" << d.nm << "], rc = "
<< d.refcount;
}
};
class DogHouse {
Dog* p;
string houseName;
public:
DogHouse(Dog* dog, const string& house)
: p(dog), houseName(house) {
cout << "Created DogHouse: "<< *this << endl;
}
DogHouse(const DogHouse& dh)
: p(dh.p),
houseName("copy-constructed " +
dh.houseName) {
p->attach();
cout << "DogHouse copy-constructor: "
<< *this << endl;
}
DogHouse& operator=(const DogHouse& dh) {
// Check for self-assignment:
if(&dh != this) {
houseName = dh.houseName + " assigned";
// Clean up what you're using first:
p->detach();
p = dh.p; // Like copy-constructor
p->attach();
}
cout << "DogHouse operator= : "
<< *this << endl;
return *this;
}
// Decrement refcount, conditionally destroy
~DogHouse() {
cout << "DogHouse destructor: "
<< *this << endl;
p->detach();
}
void renameHouse(const string& newName) {
houseName = newName;
}
void unalias() { p = p->unalias(); }
// Copy-on-write. Anytime you modify the
// contents of the pointer you must
// first unalias it:
void renameDog(const string& newName) {
unalias();
p->rename(newName);
}
// ... or when you allow someone else access:
Dog* getDog() {
unalias();
return p;
}
friend ostream&
operator<<(ostream& os, const DogHouse& dh) {
return os << "[" << dh.houseName
<< "] contains " << *dh.p;
}
};
int main() {
DogHouse
fidos(Dog::make("Fido"), "FidoHouse"),
spots(Dog::make("Spot"), "SpotHouse");
cout << "Entering copy-construction" << endl;
DogHouse bobs(fidos);
cout << "After copy-constructing bobs" << endl;
cout << "fidos:" << fidos << endl;
cout << "spots:" << spots << endl;
cout << "bobs:" << bobs << endl;
cout << "Entering spots = fidos" << endl;
spots = fidos;
cout << "After spots = fidos" << endl;
cout << "spots:" << spots << endl;
cout << "Entering self-assignment" << endl;
bobs = bobs;
cout << "After self-assignment" << endl;
cout << "bobs:" << bobs << endl;
// Comment out the following lines:
cout << "Entering rename(\"Bob\")" << endl;
bobs.getDog()->rename("Bob");
cout << "After rename(\"Bob\")" << endl;
getchar();
} ///:~
类Dog是DogHouse指向的对象,包含一个引用计数及控制和读引用计数的函数
函数attach()增加一个Dog引用计数用以指示有另一个对象使用它
在进行任何修改之前,必须保证所修改的Dog没有被别的对象正在使用
拷贝构造函数给源对象Dog赋值Dog,而不是创建它自己的存储单元
operator=处理等号左侧已创建的对象,所以operator=首先必须通过为Dog调用detach()
来整理这个存储单元
析构函数调用detach()有条件销毁Dog
为了实现写拷贝,必须控制所有写存储单元的动作
在main()中测试了几个必须正确实现引用计数的函数:构造函数、拷贝构造函数、
operator=和析构函数
输出
Creating Dog: [Fido], rc = 1
Created DogHouse: [FidoHouse] contains [Fido], rc = 1
Creating Dog: [Spot], rc = 1
Created DogHouse: [SpotHouse] contains [Spot], rc = 1
Entering copy-construction
Attached Dog: [Fido], rc = 2
DogHouse copy-constructor: [copy-constructed FidoHouse] contains [Fido], rc = 2
After copy-constructing bobs
fidos:[FidoHouse] contains [Fido], rc = 2
spots:[SpotHouse] contains [Spot], rc = 1
bobs:[copy-constructed FidoHouse] contains [Fido], rc = 2
Entering spots = fidos
Detaching Dog: [Spot], rc = 1
Deleting Dog: [Spot], rc = 0
Attached Dog: [Fido], rc = 3
DogHouse operator= : [FidoHouse assigned] contains [Fido], rc = 3
After spots = fidos
spots:[FidoHouse assigned] contains [Fido], rc = 3
Entering self-assignment
DogHouse operator= : [copy-constructed FidoHouse] contains [Fido], rc = 3
After self-assignment
bobs:[copy-constructed FidoHouse] contains [Fido], rc = 3
Entering rename("Bob")
Unaliasing Dog: [Fido], rc = 3
Dog copy-constructor: [Fido copy], rc = 1
Dog renamed to: [Bob], rc = 1
After rename("Bob")