Created
May 15, 2015 07:34
-
-
Save rickyzhang-cn/38f1864d18bc7243d6bd to your computer and use it in GitHub Desktop.
Reference count, copy-on-write
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: 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; | |
} ///:~ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
运行结果:
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")
DogHouse destructor: [copy-constructed FidoHouse] contains [Bob], rc = 1
Detaching Dog: [Bob], rc = 1
Deleting Dog: [Bob], rc = 0
DogHouse destructor: [FidoHouse assigned] contains [Fido], rc = 2
Detaching Dog: [Fido], rc = 2
DogHouse destructor: [FidoHouse] contains [Fido], rc = 1
Detaching Dog: [Fido], rc = 1
Deleting Dog: [Fido], rc = 0