Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

The curiously recurring template pattern

Any novice C++ programmer can figure out how to modify a class to keep track of the number of objects of that class that currently exist. All you have to do is to add static members, and modify constructor and destructor logic, as follows:

//: C05:CountedClass.cpp
// Object counting via static members.
#include <iostream>
using namespace std;
 
class CountedClass {
static int count;
public:
CountedClass() { ++count; }
CountedClass(const CountedClass&) { ++count; }
~CountedClass() { --count; }
static int getCount() { return count; }
};
 
int CountedClass::count = 0;
 
int main() {
CountedClass a;
cout << CountedClass::getCount() << endl; // 1
CountedClass b;
cout << CountedClass::getCount() << endl; // 2
{ // An arbitrary scope:
CountedClass c(b);
cout << CountedClass::getCount() << endl; // 3
a = c;
cout << CountedClass::getCount() << endl; // 3
}
cout << CountedClass::getCount() << endl; // 2
} ///:~
 

All constructors of CountedClass increment the static data member count, and the destructor decrements it. The static member function getCount( ) yields the current number of objects.

It would be tedious to manually add these members every time you wanted to add object counting to a class. The usual object-oriented device used to repeat or share code is inheritance, which is only half a solution in this case. Observe what happens when we collect the counting logic into a base class.

//: C05:CountedClass2.cpp
// Erroneous attempt to count objects.
#include <iostream>
using namespace std;
 
class Counted {
static int count;
public:
Counted() { ++count; }
Counted(const Counted&) { ++count; }
~Counted() { --count; }
static int getCount() { return count; }
};
 
int Counted::count = 0;
 
class CountedClass : public Counted {};
class CountedClass2 : public Counted {};
 
int main() {
CountedClass a;
cout << CountedClass::getCount() << endl; // 1
CountedClass b;
cout << CountedClass::getCount() << endl; // 2
CountedClass2 c;
cout << CountedClass2::getCount() << endl; // 3 (Error)
} ///:~
 

All classes that derive from Counted share the same, single static data member, so the number of objects is tracked collectively across all classes in the Counted hierarchy. What is needed is a way to automatically generate a different base class for each derived class. This is accomplished by the curious template construct illustrated below:

//: C05:CountedClass3.cpp
#include <iostream>
using namespace std;
 
template<class T> class Counted {
static int count;
public:
Counted() { ++count; }
Counted(const Counted<T>&) { ++count; }
~Counted() { --count; }
static int getCount() { return count; }
};
 
template<class T> int Counted<T>::count = 0;
 
// Curious class definitions
class CountedClass : public Counted<CountedClass> {};
class CountedClass2 : public Counted<CountedClass2> {};
 
int main() {
CountedClass a;
cout << CountedClass::getCount() << endl; // 1
CountedClass b;
cout << CountedClass::getCount() << endl; // 2
CountedClass2 c;
cout << CountedClass2::getCount() << endl; // 1 (!)
} ///:~
 

Each derived class derives from a unique base class that is determined by using itself (the derived class) as a template parameter! This may seem like a circular definition, and it would be, had any base class members used the template argument in a computation. Since no data members of Counted are dependent on T, the size of Counted (which is zero!) is known when the template is parsed. So it doesn t matter which argument is used to instantiate Counted because the size is always the same. Any derivation from an instance of Counted can be completed when it is parsed, and there is no recursion. Since each base class is unique, it has its own static data, thus constituting a handy technique for adding counting to any class whatsoever. Jim Coplien was the first to mention this interesting derivation idiom in print, which he cited in an article, entitled Curiously Recurring Template Patterns. [72]

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire