8.4. Автоматическое добавление новых экземпляров класса в контейнер

8.4. Автоматическое добавление новых экземпляров класса в контейнер

Проблема

Требуется хранить все экземпляры класса в едином контейнере, не требуя от пользователей класса выполнения каких-либо специальных операций.

Решение

Включите в класс статический член, являющийся контейнером, таким как list, определенный в <list>. Добавьте в этот контейнер адрес объекта при его создании и удалите его при уничтожении. Пример 8.4 показывает, как это делается.

Пример 8.4. Отслеживание объектов

#include <iostream>

#include <list>

#include <algorithm>

using namespace std;

class MyClass {

protected:

 int value_;

public:

 static list<MyClass*> instances_;

 MyClass(int val);

 ~MyClass();

 static void showList();

};

list<MyClass*> MyClass::instances_;

MyClass::MyClass(int val) {

 instances_.push_back(this);

 value_ = val;

}

MyClass::~MyClass() {

 list<MyClass*>::iterator p =

  find(instances_.begin(), instances_.end(), this);

 if (p != instances_.end()) instances_.erase(p);

}

void MyClass::showList() {

 for (list<MyClass*>::iterator p = instances_.begin();

  p != instances_.end(); ++p)

  cout << (*p)->value_ << endl;

}

int main() {

 MyClass a(1);

 MyClass b(10);

 MyClass с(100);

 MyClass::showList();

}

Пример 8.4 создаст следующий вывод.

1

10

100

Обсуждение

Подход в примере 8.4 очень прост: используйте для хранения указателей на объекты static list. При создании объекта его адрес добавляется в list; при его уничтожении он удаляется. Здесь имеется пара важных моментов.

При использовании любых членов-данных типа static их требуется объявлять в заголовочном файле класса и определять в файле реализации. Пример 8.4 весь находится в одном файле, так что здесь это не применимо, но помните, что переменную типа static требуется определять в файле реализации, а не в заголовочном файле. За объяснением причин обратитесь к рецепту 8.5.

Вы не обязаны использовать член static. Конечно, можно использовать глобальный объект, но тогда дизайн не будет таким «замкнутым». Более того, вам где-то еще придется выделять память для глобального объекта, передавать его в конструктор MyClass и в общем случае выполнять еще целый ряд действий.

Помните, что совместное использование глобального контейнера, как в примере 8.4, не будет работать, если объекты класса MyClass создаются в нескольких потоках. В этом случае требуется сериализация доступа к общему объекту через мьютексы. Рецепты, относящиеся к этой и другим методикам многопоточности, приведены в главе 12.

Если требуется отслеживать все экземпляры класса, можно также использовать шаблон фабрики. В целом это будет означать, что для создания нового объекта клиентский код вместо вызова оператора new должен будет вызывать функцию. За подробностями о том, как это делается, обратитесь к рецепту 8.2.

Смотри также

Рецепт 8.2.