Статические методы и переменные
Часть раздела Объектно-ориентированное программирование путешествия по 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);Статические методы идеально подходят для вспомогательных функций, которым не требуется состояние объекта, фабричных методов, создающих объекты, или для доступа к статическим данным. Поскольку они не требуют экземпляра объекта, вы вызываете их, используя имя класса и оператор разрешения области видимости: ClassName::methodName().
Задание
ЛегкоДавайте создадим систему генерации уникальных идентификаторов, которая демонстрирует, как статические методы и переменные работают вместе, обеспечивая функциональность на уровне класса без необходимости создания экземпляров объектов.
Вы создадите два файла для организации вашего кода:
IDGenerator.h: Определите классIDGenerator, который генерирует последовательные уникальные идентификаторы. Ваш класс должен содержать:- Приватный статический член
nextId, который отслеживает следующий назначаемый ID (начинается с1000) - Приватный статический член
prefix(строка), который хранит настраиваемый префикс для всех ID - Статический метод
setPrefix(const std::string& p), который изменяет префикс - Статический метод
getNextId(), который возвращает строку, объединяющую префикс и текущий номер ID, а затем увеличиваетnextIdдля следующего вызова - Статический метод
peekNextId(), который возвращает то, каким будет следующий ID, без увеличения счетчика - Статический метод
getGeneratedCount(), который возвращает количество сгенерированных ID (подсказка: вычислите на основе начального значения и текущегоnextId) - Статический метод
reset(), который сбрасываетnextIdобратно к1000
Не забудьте определить статические члены вне класса после определения класса. Инициализируйте
prefixзначением"ID-".- Приватный статический член
main.cpp: Продемонстрируйте вызов статических методов, используя имя класса без создания объектов. Считайте пользовательский префикс из входных данных, затем:- Выведите
"Peek: <peeked_id>", используяpeekNextId() - Сгенерируйте три ID с помощью
getNextId()и выведите каждый в формате"Generated: <id>" - Выведите
"Count: <count>", показывая, сколько ID было сгенерировано - Измените префикс, используя введенное значение с помощью
setPrefix() - Сгенерируйте еще два ID и выведите каждый в формате
"Generated: <id>" - Снова выведите
"Count: <count>" - Вызовите
reset() - Выведите
"After reset - Peek: <peeked_id>" - Выведите
"After reset - Count: <count>"
- Выведите
Обратите внимание, что вы никогда не создаете объект IDGenerator — все методы вызываются непосредственно у класса с помощью IDGenerator::methodName(). Это ключевая характеристика статических методов: они принадлежат самому классу, а не какому-либо конкретному экземпляру.
Включите <string> и <iostream> в ваш заголовочный файл и не забудьте про guard-вставки (header guards).
Шпаргалка
Статические переменные, объявленные внутри класса, должны быть определены вне класса. Статическим членам требуется ровно одно место хранения, разделяемое всеми объектами:
class Counter {
static int count; // Только объявление
public:
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);Статические методы вызываются с использованием имени класса и оператора разрешения области видимости: ClassName::methodName(). Они идеально подходят для вспомогательных функций, которым не требуется состояние объекта, фабричных методов или доступа к статическим данным.
Попробуйте сами
#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: Вывести "After reset - Peek: <peeked_id>"
// TODO: Вывести "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Итоги — Обобщенный контейнер