Члены экземпляра против статических
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy — урок 27 из 104.
До сих пор каждая переменная-член, которую вы создавали, принадлежала конкретному объекту. Когда вы создаете два объекта Player, у каждого из них есть свои собственные name и health. Они называются членами экземпляра — они существуют отдельно для каждого экземпляра класса.
Но что, если вам нужны данные, общие для всех объектов? Например, для отслеживания общего количества существующих игроков.
Вот здесь и появляются статические члены. Статический член принадлежит самому классу, а не какому-либо конкретному объекту.
class Player {
std::string name; // Член экземпляра - у каждого игрока свой собственный
int health; // Член экземпляра
static int playerCount; // Статический член - общий для ВСЕХ игроков
public:
Player(std::string n) : name(n), health(100) {
playerCount++; // Увеличивается при создании любого игрока
}
~Player() {
playerCount--; // Уменьшается при уничтожении любого игрока
}
};Ключевое отличие: члены экземпляра доступны через объект, в то время как статические члены доступны через само имя класса. Представьте члены экземпляра как «один на объект», а статические члены — как «один на класс».
| Члены экземпляра | Статические члены |
|---|---|
| Уникальны для каждого объекта | Общие для всех объектов |
| Создаются при создании объекта | Существуют до создания любого объекта |
Доступ через объект: player.health | Доступ через класс: Player::playerCount |
Статические члены полезны для счетчиков, конфигурационных значений или любых данных, которые логически принадлежат классу, а не отдельным объектам.
Задание
ЛегкоДавайте создадим систему инвентаризации товаров, которая отслеживает как детали отдельных продуктов, так и общую статистику инвентаря, используя экземпляры и статические члены.
Вы создадите два файла для организации вашего кода:
Product.h: Определите классProduct, который управляет отдельными продуктами и одновременно отслеживает данные по всему инвентарю. Ваш класс должен иметь:- Члены экземпляра:
name(string) иprice(double) — уникальные для каждого продукта - Статический член
totalProducts, который подсчитывает, сколько продуктов существует в данный момент - Статический член
totalValue, который отслеживает общую стоимость всех продуктов - Конструктор, который принимает имя и цену, инициализирует члены экземпляра и обновляет оба статических счетчика (увеличивает количество, добавляет цену к общей стоимости)
- Деструктор, который обновляет статические счетчики при удалении продукта (уменьшает количество, вычитает цену из общей стоимости)
- Метод
getInfo(), который возвращает строку в формате:"<name>: "<name>: $<price>"lt;price>" - Статический метод
getInventoryStats(), который возвращает строку:"Products: <count>, Total Value: "Products: <count>, Total Value: $<value>"lt;value>"
- Члены экземпляра:
main.cpp: Продемонстрируйте, как статические члены разделяются между всеми объектами, в то время как члены экземпляра остаются уникальными. Считайте два названия продуктов и их цены из входных данных (name1, price1, name2, price2 — каждое на отдельной строке), затем:- Выведите начальную статистику инвентаря, используя имя класса:
Product::getInventoryStats() - Создайте блок области видимости с помощью фигурных скобок и внутри него создайте объект
Productс первым именем и ценой. Выведите информацию о нем и текущую статистику инвентаря. - После завершения блока (первый продукт уничтожен), выведите
"After first scope:", а затем статистику инвентаря - Создайте еще один
Productсо вторым именем и ценой. Выведите информацию о нем и статистику инвентаря. - Создайте третий
Productс именем"Bonus"и ценой5.00. Выведите финальную статистику инвентаря.
- Выведите начальную статистику инвентаря, используя имя класса:
Не забудьте определить статические члены вне класса в заголовочном файле (после определения класса). Форматируйте цены с двумя знаками после запятой, используя std::fixed и std::setprecision(2) из библиотеки <iomanip>.
Подключите ваш заголовочный файл в main.cpp с помощью #include "Product.h".
Шпаргалка
C++ поддерживает два типа членов класса: члены экземпляра и статические члены.
Члены экземпляра уникальны для каждого объекта — у каждого экземпляра есть своя собственная копия:
class Player {
std::string name; // У каждого игрока есть свое имя
int health; // У каждого игрока есть свое здоровье
};Статические члены являются общими для всех объектов класса — существует только одна копия, которая принадлежит самому классу:
class Player {
static int playerCount; // Общий для ВСЕХ игроков
public:
Player(std::string n) : name(n), health(100) {
playerCount++; // Увеличиваем общий счетчик
}
~Player() {
playerCount--; // Уменьшаем при уничтожении
}
};Ключевые различия:
| Члены экземпляра | Статические члены |
|---|---|
| Уникальны для каждого объекта | Общие для всех объектов |
| Создаются при создании объекта | Существуют до создания любого объекта |
Доступ через объект: player.health | Доступ через класс: Player::playerCount |
Статические методы можно вызывать, используя имя класса, и они могут обращаться только к статическим членам:
class Player {
static int playerCount;
public:
static int getPlayerCount() {
return playerCount; // Могут обращаться только к статическим членам
}
};
// Вызов с использованием имени класса
int count = Player::getPlayerCount();Статические члены должны быть определены вне класса (обычно в файле .cpp или после определения класса в заголовочном файле):
int Player::playerCount = 0;Статические члены полезны для счетчиков, общих значений конфигурации или любых данных, которые логически принадлежат классу, а не отдельным объектам.
Попробуйте сами
#include <iostream>
#include <string>
#include "Product.h"
using namespace std;
int main() {
// Чтение входных данных
string name1;
double price1;
string name2;
double price2;
getline(cin, name1);
cin >> price1;
cin.ignore();
getline(cin, name2);
cin >> price2;
// TODO: Выведите начальную статистику инвентаря, используя Product::getInventoryStats()
// TODO: Создайте блок области видимости с фигурными скобками
// Внутри блока:
// - Создайте Product с name1 и price1
// - Выведите информацию о нем, используя getInfo()
// - Выведите текущую статистику инвентаря
// TODO: После завершения блока выведите "After first scope:"
// Затем выведите статистику инвентаря
// TODO: Создайте еще один Product с name2 и price2
// Выведите информацию о нем и статистику инвентаря
// TODO: Создайте третий Product с именем "Bonus" и ценой 5.00
// Выведите итоговую статистику инвентаря
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Итоги — Обобщенный контейнер