Статические методы и переменные
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 31 из 104.
Вы узнали, что статические члены являются общими для всех объектов. Теперь давайте рассмотрим, как правильно определять и использовать статические методы наряду со статическими переменными.
Статические переменные, объявленные внутри класса, должны быть определены вне класса, обычно в исходном файле. Это связано с тем, что статическим членам необходимо ровно одно место хранения, общее для всех объектов:
class Counter {
static int count; // Только объявление
public:
Counter() { count++; }
~Counter() { count--; }
static int getCount() { // Статический метод
return count;
}
};
// Определение — требуется вне класса
int Counter::count = 0;Статический метод принадлежит классу, а не какому-либо объекту. Это имеет важное следствие: статические методы не могут обращаться к членам экземпляра или использовать указатель this, потому что они не вызываются для какого-либо конкретного объекта.
class MathUtils {
static double pi;
public:
static double circleArea(double radius) {
return pi * radius * radius; // Может обращаться к статическим членам
}
};
double MathUtils::pi = 3.14159;
// Вызывается без объекта
double area = MathUtils::circleArea(5.0);Статические methods идеально подходят для служебных функций, которым не требуется состояние объекта, factory methods, создающих объекты, или доступа к статическим данным. Поскольку им не нужен экземпляр объекта, их вызывают с использованием имени class и оператора разрешения области видимости: ClassName::methodName().
Задание
ЛегкоДавайте создадим систему генерации уникальных ID, которая демонстрирует, как статические методы и переменные работают вместе, обеспечивая функциональность на уровне class без необходимости создавать экземпляры объектов.
Вы создадите два файла, чтобы организовать свой код:
IDGenerator.h: Определите classIDGenerator, который генерирует последовательные уникальные ID. В вашем class должны быть:- Приватный статический member
nextId, отслеживающий следующий ID, который будет назначен (начинается с1000) - Приватный статический member
prefix(строка), хранящий настраиваемый префикс для всех ID - Статический method
setPrefix(const std::string& p), изменяющий префикс - Статический method
getNextId(), возвращающий строку, объединяющую префикс и текущий номер ID, а затем увеличивающийnextIdдля следующего вызова - Статический method
peekNextId(), возвращающий значение следующего ID без увеличения счётчика - Статический method
getGeneratedCount(), возвращающий количество сгенерированных ID (подсказка: вычислите его на основе начального значения и текущегоnextId) - Статический method
reset(), возвращающийnextIdобратно к1000
Не забудьте определить статические members за пределами class после определения class. Инициализируйте
prefixзначением"ID-".- Приватный статический member
main.cpp: Продемонстрируйте вызов статических methods с использованием имени class без создания каких-либо объектов. Прочитайте пользовательский префикс из input, затем:- Выведите
"Peek: <peeked_id>"с помощьюpeekNextId() - Сгенерируйте три ID с помощью
getNextId()и выведите каждый в виде"Generated: <id>" - Выведите
"Count: <count>", показывая, сколько ID было сгенерировано - Измените префикс, используя значение из input и
setPrefix() - Сгенерируйте ещё два ID и выведите каждый в виде
"Generated: <id>" - Снова выведите
"Count: <count>" - Вызовите
reset() - Выведите
"After reset - Peek: <peeked_id>" - Выведите
"After reset - Count: <count>"
- Выведите
Обратите внимание, что вы никогда не создаёте объект IDGenerator: все methods вызываются непосредственно у class с использованием IDGenerator::methodName(). Это ключевая характеристика статических methods: они принадлежат самому class, а не какому-либо конкретному экземпляру.
Подключите <string> и <iostream> в своём заголовочном файле и не забудьте про защиту заголовочного файла.
Попробуйте сами
#include <iostream>
#include <string>
#include "IDGenerator.h"
using namespace std;
int main() {
// Прочитать пользовательский префикс из ввода
string customPrefix;
cin >> customPrefix;
// TODO: Вывести "Peek: <peeked_id>" с помощью peekNextId()
// TODO: Сгенерировать три ID с помощью getNextId() и вывести каждый как "Generated: <id>"
// TODO: Вывести "Count: <count>", показывая, сколько ID было сгенерировано
// TODO: Изменить префикс, используя значение из ввода, с помощью setPrefix()
// TODO: Сгенерировать ещё два ID и вывести каждый как "Generated: <id>"
// TODO: Вывести "Count: <count>" снова
// TODO: Вызвать reset()
// TODO: Print "After reset - Peek: <peeked_id>"
// TODO: Print "After reset - Count: <count>"
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнерПотренируйтесь самостоятельно: Онлайн-компилятор C++