Читаем Thinking In C++. Volume 2: Practical Programming полностью

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 number of current objects whenever it called.

It would be tremendously tedious to have to manually add these members every time you wanted to add object counting to a class. What is the usual object-oriented device to which one turns to repeat or share code? It’s inheritance, of course, which, unfortunately, 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

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

using namespace std;

template

class Counted {

  static int count;

public:

  Counted() { ++count; }

  Counted(const Counted&) { ++count; }

  ~Counted() { --count; }

  static int getCount() { return count; }

};

template

int Counted::count = 0;

// Curious class definitions

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;   // 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 all data members of Counted are not dependent on T, its size (which is zero!) is known when the template is parsed. It doesn’t matter, therefore, which argument is used to instantiate Counted; its size is always the same. Therefore, 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."[ ][65] 

<p>Template metaprogramming</p>

In 1993 compilers were beginning to support simple template constructs so that users could define generic containers and functions. About the same time that the STL was being considered for adoption into standard C++, clever and surprising examples such as the following were passed around among members of the standards committee:.

//: C05:Factorial.cpp

// Compile-time computation!

#include

using namespace std;

template

struct Factorial {

   enum {val = Factorial::val * n};

};

template<>

struct Factorial<0> {

   enum {val = 1};

};

int main() {

   cout << Factorial<12>::val << endl; // 479001600

} ///:~

That this program prints the correct value of 12! is not alarming. What is alarming is that the computation is complete before the program even runs!.

Перейти на страницу:

Похожие книги

3ds Max 2008
3ds Max 2008

Одни уверены, что нет лучшего способа обучения 3ds Мах, чем прочитать хорошую книгу. Другие склоняются к тому, что эффективнее учиться у преподавателя, который показывает, что и как нужно делать. Данное издание объединяет оба подхода. Его цель – сделать освоение 3ds Мах 2008 максимально быстрым и результативным. Часто после изучения книги у читателя возникают вопросы, почему не получился тот или иной пример. Видеокурс – это гарантия, что такие вопросы не возникнут: ведь автор не только рассказывает, но и показывает, как нужно работать в 3ds Мах.В отличие от большинства интерактивных курсов, где работа в 3ds Мах иллюстрируется на кубиках-шариках, данный видеокурс полностью практический. Все приемы работы с инструментами 3ds Мах 2008 показаны на конкретных примерах, благодаря чему после просмотра курса читатель сможет самостоятельно выполнять даже сложные проекты.

Владимир Антонович Верстак , Владимир Верстак

Программирование, программы, базы данных / Программное обеспечение / Книги по IT