int UniqueID::nextID = 0;
UniqueID::UniqueID() {
id = ++nextID;
}
UniqueID::UniqueID(const UniqueID& orig) {
id = orig.id;
}
UniqueID& UniqueID::operator=(const UniqueID& orig) {
id = orig.id;
return(*this);
}
int main() {
UniqueID a;
std::cout << a.id << std::endl;
UniqueID b;
std::cout << b.id << std::endl;
UniqueID c;
std::cout << c.id << std::endl;
}
Для отслеживания следующего доступного для использования идентификатора используйте статическую переменную. В примере 8.8 используется static int
В данном случае идентификаторы не используются повторно до тех пор, пока не будет достигнуто максимально возможное для целого числа значение. При удалении объекта его уникальное значение пропадает либо до перезапуска программы, либо до переполнения значения идентификатора. Эта уникальность в программе может иметь несколько интересных преимуществ. Например, при работе с библиотекой управления памятью, которая перемещает блоки памяти и обновляет значения указателей, можно быть уверенным, что для каждого объекта будет сохранено его первоначальное уникальное значение. При использовании уникальных значений в сочетании с рецептом 8.4, но применении map
list
можно легко найти объект с заданным уникальным номером. Чтобы сделать это, просто отобразите уникальные ID на экземпляры объектов, как здесь.static map
Таким образом любой код, который отслеживает идентификаторы объектов, всегда сможет найти его без необходимости хранить ссылку на него.
Но это еще не все. Рассмотрим случай, когда один из этих объектов требуется добавить в стандартный контейнер (vector
list
, set
и т.п.). Стандартные контейнеры хранят копии объектов, добавляемых в них, а не ссылки или указатели на эти объекты (конечно, при условии, что это не контейнер указателей). Таким образом, стандартные контейнеры ожидают, что объекты, которые в них содержатся, ведут себя как объекты Это означает, что требуется решить, как должны себя вести уникальные объекты. При создании объекта с уникальным идентификатором и добавлении его в контейнер у вас появятся два объекта с одним и тем же идентификатором при условии, что вы не переопределили оператор присвоения. В операторе присвоения и конструкторе копирования требуется выполнить те действия с уникальным значением, которые имеют смысл для вашего случая. Имеет ли смысл то, что объект в контейнере будет равен оригинальному объекту? Если да, то вполне подойдет стандартный конструктор копирования и оператор присвоения, но вы должны указать это явно, чтобы пользователи вашего класса знали, что вы делаете это намеренно, а не просто забыли, как работают контейнеры. Например, чтобы использовать одно и то же значение идентификатора, конструктор копирования и оператор присвоения должны выглядеть вот так.
UniqueID::UniqueID(const UniqueID& orig) {
id = orig.id;
}
UniqueID& UniqueID::operator=(const UniqueID& orig) {
id = orig.id;
return(*this);
}
Но может возникнуть ситуация, когда в контексте приложения будет иметь смысл создать для объекта в контейнере новое уникальное значение. В этом случае просто снова используйте статическую переменную, как это сделано в обычном конструкторе и показано здесь.
UniqueID::UniqueID(const UniqueID& orig) {
id = ++nextID;
}
UniqueID& UniqueID::operator=(const UniqueID& orig) {
id = ++nextID;
return(*this);
}
Однако трудности еще не закончились. Если UniqueID
Рецепт 8.3.
8.9. Создание Singleton-класса
Имеется класс, который должен иметь только один экземпляр, и требуется предоставить способ доступа к этому классу из клиентского кода таким образом, чтобы каждый раз возвращался именно этот единственный объект. Часто это называется шаблоном