Menu
Coddy logo textTech

Статические методы и переменные

Часть раздела Объектно-ориентированное программирование путешествия по 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().

challenge icon

Задание

Легко

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

Вы создадите два файла для организации вашего кода:

  • 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;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование