Статические поля и методы
Статические методы и статические переменные
Рассмотрим ещё раз класс Box. Здесь для простоты все поля сделаны публичными:
Box.h
#ifndef _BOX_H_ #define _BOX_H_ class Box { public: unsigned height; unsigned depth; unsigned width; public: Box(); Box(unsigned, unsigned, unsigned); ~Box(); unsigned getVolume(); }; #endif
Box.cpp
#include "Box.h" Box::Box(): depth(0), height(0), width(0) {} Box::Box(unsigned depth, unsigned height, unsigned width) : depth(depth), height(height), width(width) {} Box::~Box() { } unsigned Box::getVolume() { return this->depth * this->height * this->width; }
main.cpp
#include <iostream> #include "Box.h" void main() { Box a = { 10, 10, 10 }; Box b = { 20, 20, 20 }; std::cout << "a.getVolume() = " << a.getVolume() << std::endl; std::cout << "b.getVolume() = " << b.getVolume() << std::endl; system("pause"); }
Код очень простой, здесь нет никаких особых моментов. Особо хочется обратить снимание на то, что каждый экземпляр класса Box имеет собственный набор значений depth, width, height. Поля класса принадлежат экземпляру класса.
Метод getVolume, несмотря на то, что является общим для всех экземпляров класса, применяется к конкретному объекту и работает с его конкретными полями.
Кроме таких полей и методов класса можно также определять статические поля и методы. Статические члены класса принадлежат классу, а не его экземплярам. То есть, они будут являться общими для всех созданных объектов.
Например, создадим статическое поле counter, которое будет увеличиваться на 1 каждый раз, когда создаётся новый экземпляр.
Статическое поле создаётся с помощью служебного слова static
#ifndef _BOX_H_ #define _BOX_H_ class Box { public: unsigned height; unsigned depth; unsigned width; protected: static size_t counter; public: Box(); Box(unsigned, unsigned, unsigned); ~Box(); unsigned getVolume(); }; #endif
Box.cpp
#include "Box.h" size_t Box::counter = 0; Box::Box(): depth(0), height(0), width(0) { Box::counter++; } Box::Box(unsigned depth, unsigned height, unsigned width) : depth(depth), height(height), width(width) { Box::counter++; } Box::~Box() { Box::counter--; } unsigned Box::getVolume() { return this->depth * this->height * this->width; }
Здесь нужно обратить внимание на следующие моменты.
- 1. Обращение к статическому полю ведётся через имя класса
Box::counter
Тем не менее, обращаться к статическому полю можно и через указатель thisthis->counter
-
2. Статическое поле необходимо инициализировать. Инициализация происходит в любом месте cpp файла. Для инициализации необходимо написать полное имя статической переменной, опустив static. В нашем случае
size_t Box::counter = 0;
- 3. Статическая переменная объявлена как protected. То есть, обратиться извне к ней сможет только наследник. Как и с обычными нестатическими переменными, методы класса могут без проблем обращаться к своей собственной переменной.
Теперь необходимо каким-то образом получить доступ к нашей переменной. Для этого напишем метод, возвращающий значение counter.
size_t getCounter();
size_t Box::getCounter() { return Box::counter; }
main.cpp
#include <iostream> #include "Box.h" void main() { Box a = { 10, 10, 10 }; std::cout << "a.getVolume() = " << a.getVolume() << std::endl; std::cout << "counter = " << a.getCounter() << std::endl; Box b = { 20, 20, 20 }; std::cout << "b.getVolume() = " << b.getVolume() << std::endl; std::cout << "counter = " << a.getCounter() << std::endl; system("pause"); }
В данном случае, обращение a.getCounter() эквивалентно обращению b.getCounter(), так как они обращаются к общей переменной counter.
Так как метод обращается к статическому полю, то он не нуждается в экземпляре класса для того, чтобы его можно было вызвать. Сделаем метод getCounter статическим
Изменим объявление метода
static size_t getCounter();
Определение останется тем же
size_t Box::getCounter() { return Box::counter; }
Обращаться к методу теперь можно через имя класса
#include <iostream> #include "Box.h" void main() { Box a = { 10, 10, 10 }; std::cout << "a.getVolume() = " << a.getVolume() << std::endl; std::cout << "counter = " << Box::getCounter() << std::endl; Box b = { 20, 20, 20 }; std::cout << "b.getVolume() = " << b.getVolume() << std::endl; std::cout << "counter = " << Box::getCounter() << std::endl; system("pause"); }
Тем не менее, обращаться к статическому методу можно и через экземпляр класса
std::cout << "counter = " << a.getCounter() << std::endl;
Но это плохая практика.
Если метод статический, то к нему можно обращаться до того, как был создан хотя бы один экземпляр переменной. Поэтому статический метод не может обращаться к нестатическим полям класса. Кроме того, статический метод просто не будет знать, к какому экземпляру обратиться – внутри него нет доступа до указателя this.
Например, если мы объявим метод getVolume статическим, то будет ошибка компиляции с сообщением
Оператор this можно использовать только внутри нестатической функции-члене.
Для использования статического метода, таким образом, не нужно создавать отдельного экземпляра класса, а обращение ведётся через имя класса и оператор ::
Статические поля и методы, также как и нестатические, могут иметь модификаторы доступа private, protected и public.
Добавим статический защищённый метод, который будет проверять поля переданного аргумента
static size_t getCounter();
bool Box::checkSize(const Box &o) { if (o.depth > 10000 || o.height > 10000 || o.width > 10000) { return false; } else { return true; } }
Этот метод можно переписать так
bool Box::checkSize(const Box &o) { if (o.depth > 10000 || o.height > 10000 || o.width > 10000) { return false; } return true; }
Или, воспользовавшись законом де-Моргана, так
bool Box::checkSize(const Box &o) { return o.depth <= 10000 && o.height <= 10000 && o.width <= 10000; }
При использовании статических методов запрещён модификатор const метода, так как метод по определению не может обращаться к нестатическим полям и изменять их.
