Garbage collection is a process that recovers heap memory that is no longer being referenced. Languages such as LISP, Smalltalk, and Java have built-in garbage collectors that run in the background and track object references. When an object is no longer referenced it is deleted, and the memory that it occupied is made available for use by other objects.
The next examples show a way of building garbage collection into the design of a class by means of reference counting. Reference counting is an example of resource sharing.
Each object keeps track of its active references. When an object is created, its reference counter is set to 1. Each time the object is newly referenced, the reference counter is incremented. Each time it loses a reference, the reference counter is decremented. When the reference count becomes 0, the shared object can be deallocated.
In Example 24.2, we define a homemade string class, MyString, that contains a private inner class, MyStringPrivate, which is responsible for the creation of dynamic arrays and for maintaining a reference count.
An inner class is simply a class defined inside another class. Inner classes are "private" classes, meant to be used only by the containing class.
[ . . . . ]
class MyString {
class MyStringPrivate {
friend class MyString; <-- 1
public:
MyStringPrivate() : m_Len(0), m_RefCount(1) {
m_Chars = new (nothrow) char[1] ;
m_Chars[0] = 0;
}
MyStringPrivate(const char* p) : m_RefCount(1) {
m_Len = strlen(p);
m_Chars = new (nothrow) char[m_Len + 1];
if (m_Chars)
strncpy(m_Chars, p, m_Len + 1);
else
cerr << "Out of memory in MyStringPrivate ctor!"
<< endl;
}
~MyStringPrivate() {
delete []m_Chars;
}
private:
int m_Len, m_RefCount;
char* m_Chars;
};
public:
MyString() : m_Impl(new MyStringPrivate) {}
MyString(const char* p)
: m_Impl(new MyStringPrivate(p)) {}
MyString(const MyString& str);
~MyString();
void operator=(const MyString& str);
void display() const ;
int length() const;
private:
MyStringPrivate* m_Impl;
};
[ . . . . ]
|
nothrowWe used the nothrow qualifier for new (Section 22.9.3) to avoid having to add exception handling code to the example. |
The public class MyString, because it manages shared instances of MyStringPrivate, is sometimes called a handler class. It is responsible for maintaining the correct value of the reference counter, and for deleting the pointer when the counter reaches zero.
In Example 24.3, we have output statements in the definitions of three member functions to show the reference counter as objects are created and destroyed.
[ . . . . ]
MyString::MyString(const MyString& str) : m_St(str.m_St) {
m_St -> m_RefCount++;
cout << m_St->m_S << "::refcount: " << m_St->m_RefCount << endl;
}
MyString::~MyString() {
cout << m_St->m_S << "::refcount: " << m_St->m_RefCount << endl;
if (--m_St -> m_RefCount == 0) {
cout << m_St->m_S << "::memory released" << endl;
delete m_St;
}
}
void MyString::operator=(const MyString& str) {
if (str.m_St != m_St) {
if (--m_St -> m_RefCount == 0)
delete m_St;
m_St = str.m_St; <-- 1
++(m_St->m_RefCount);
}
}
[ . . . . ]
|
The client code shown in Example 24.4 contains a function with a value parameter and a main() with an inner block. Inside the block, objects are created, copied, and destroyed.
#include "refcount.h" void fiddle(MyString lstr1) { cout << "inside fiddle()" << endl; MyString lstr2(lstr1); MyString lstr3; lstr3 = lstr2; } int main() { MyString str1("AABBCCDD"); { cout << "local block begins" << endl; MyString str2(str1); fiddle(str2); cout << "back from fiddle()" << endl; } cout << "local block ends" << endl; str1.display(); cout << endl; return 0; } |
The output that follows dispassionately shows the entire saga of birth and death as the process races from opening brace to closing brace.
local block begins AABBCCDD::refcount: 2 AABBCCDD::refcount: 3 inside fiddle() AABBCCDD::refcount: 4 AABBCCDD::refcount: 5 AABBCCDD::refcount: 4 AABBCCDD::refcount: 3 back from fiddle() AABBCCDD::refcount: 2 local block ends AABBCCDD AABBCCDD::refcount: 1 AABBCCDD::memory released src/mystring>
1. | Example 24.3 demonstrates reference counting but does not deal with the question of what to do if a MyString object needs to have its value changed when its reference counter is greater than 1. How would you implement the cloning of a MyString object when necessary (but only when necessary)? Implement your solution and test it. |
2. | Implement a thread-safe version using QMutex. |
3. | Rewrite MyString using QSharedData and QSharedDataPointer. |
Part I: Introduction to C++ and Qt 4
C++ Introduction
Classes
Introduction to Qt
Lists
Functions
Inheritance and Polymorphism
Part II: Higher-Level Programming
Libraries
Introduction to Design Patterns
QObject
Generics and Containers
Qt GUI Widgets
Concurrency
Validation and Regular Expressions
Parsing XML
Meta Objects, Properties, and Reflective Programming
More Design Patterns
Models and Views
Qt SQL Classes
Part III: C++ Language Reference
Types and Expressions
Scope and Storage Class
Statements and Control Structures
Memory Access
Chapter Summary
Inheritance in Detail
Miscellaneous Topics
Part IV: Programming Assignments
MP3 Jukebox Assignments
Part V: Appendices
MP3 Jukebox Assignments
Bibliography
MP3 Jukebox Assignments