Статические элементы класса С помощью модификатора static можно описать статические поля и методы класса. Их можно рассматривать как глобальные переменные и функции, доступные только в пределах класса. Статические поля Статические поля применяются для хранения данных, общих для всех объектов класса. Эти поля хранятся отдельно от объекта и существуют для всех объектов класса в единственном экземпляре. Эти поля хранятся отдельно от объекта и существуют для всех объектов класса в единственном экземпляре. Особенности статических полей: Память под статическое поле выделяется один раз при его инициализации независимо от числа созданных объектов и инициализируется с помощью операции доступа к области действия, а не операции выбора (определение должно быть записано вне функции); Статические константы можно инициализировать при описании в классе; Статические поля доступны как через имя класса, так и через имя объекта; На статические поля распространяется действие спецификаторов доступа, поэтому статические поля, описанные как private, нельзя изменять с помощью операции доступа к области действия. Это можно сделать только с помощью статических методов; Память, занимаемая статическим полем, не учитывается при определении размера объекта с помощью операции sizeof. Пример определения статического поля class A { public: static int count; // Объявление в классе }; ... int A::count; // Определение в глобальной области // По умолчанию инициализируется нулем // int A::count =10; Пример инициализации произвольным значением Пример обращения к статическому полю A *a, b; ... cout << A::count <<a->count << b.count Статические методы Статические методы предназначены для обращения к статическим полям класса. Они могут обращаться непосредственно только к статическим полям и вызывать только другие статические методы класса, потому что им не передается скрытый указатель this. Обращение к статическим методам выполняется так же, как и к статическим полям: либо через имя класса, либо, если хотя бы один объект класса уже создан, через имя объекта. Статические методы не могут быть константными и виртуальными. Зато они могут изменять значения статических полей даже для константных объектов. Пример использования статического метода. class A { static int count; // Поле count ─ скрытое public: static void inc_count(){ count++;} }; ... int A::count; // Определение в глобальной области void f() { A a; // a.count++ ─ нельзя, так поле count ─ скрытое //Изменение поля с помощью статического метода: a.inc_count(); // или A::inc_count(); } Дружественные функции и классы Дружественные функции и дружественные классы используются для расширения интерфейса класса. Дружественная функция Дружественная функция применяется для доступа к скрытым полям класса и представляет собой альтернативу методам. Метод, как правило, описывает свойство объекта, а в виде дружественных функций оформляются действия, не являющиеся свойствами класса, но концептуально входящие в его интерфейс и нуждающиеся в доступе к его скрытым полям, например переопределение операции вывода объектов. Дружественная функция объявляется внутри класса, к элементам которого ей нужен доступ, с ключевым словом friend. В качестве параметра ей должен передаваться объект или ссылка на объект класса, поскольку указатель this ей не передается. Дружественная функция может быть обычной функцией или методом другого ранее определенного класса. На нее не распространяется действие спецификаторов доступа, место размещения ее объявления в классе безразлично. Одна функция может быть дружественной сразу нескольким классам. Пример: class monster; //Предварительное объявление класса class hero { public: void kill (monster &); ... }; class monster { ... friend int steal_ammo (monster &); friend void hero:: kill(monster &); // Класс hero должен быть определен ранее }; int steal ammo (monster &M) {return --M.ammo;} void hero::kill(monster &M) {M.health = 0; M.ammo = 0;} В данном примере описаны две функции, дружественные классу monster. Функция kill является методом класса hero, а функция steal_ammo не принадлежит ни одному классу. Обеим функциям в качестве параметра передается ссылка на объект класса monster. Использование дружественных функций нужно по возможности избегать, поскольку они нарушают принцип инкапсуляции и таким образом затрудняют отладку и модификацию программ. Дружественный класс Если все методы какого-либо класса должны иметь доступ к скрытым полям другого класса, весь класс объявляется дружественным с помощью ключевого слова friend. Объявление friend не является спецификатором доступа и не наследуется. Класс сам определяет, какие функции и классы являются для него дружественными. Пример class hero { ... friend class mistress; } class mistress { ... void f1(); void f2(); } В данном примере класс mistress объявляется дружественным классу hero. Функции f1 и f2 являются дружественными по отношению к классу hero, хотя и описаны без ключевого слова friend, и имеют доступ ко всем его полям. Деструкторы Деструктор ─ это особый вид метода, применяющийся для уничтожения объекта. Деструктор вызывается автоматически: для локальных объектов ─ при выходе из блока, в котором они объявлены; для глобальных объектов ─ как часть процедуры выхода из main; для объектов, заданных через указатель, ─ при использовании операции delete. Деструктор объекта не вызывается при выходе из области действия указателя на него. Имя деструктора начинается с тильды (~), за которой следует имя класса. Деструктор: не имеет параметров и возвращаемого значения; не может быть объявлен как const или static; не наследуется; может быть виртуальным Если деструктор явным образом не определен, компилятор автоматически создает пустой деструктор. Конструкторы класса не запрашивают ресурсов, которые нужно возвращать (например, динамическую память), или не выполняет инициализирующих действий, которые при уничтожении объекта требует завершающих действий (например, если файл открывается, его нужно закрывать), то деструктор можно не описывать. Операции класса С++ позволяет переопределить действие большинства операций так, чтобы при использовании с объектами конкретного класса они выполняли заданные функции. Это дает возможность задействовать собственные типы данных точно так же, как стандартные. Обозначения собственных операций вводить нельзя. Можно переопределять любые операции С++, за исключением следуюих: .* ?: :: # ## sizeof Перегрузка операций осуществляется с помощью функций-операций и подчиняется следующим правилам: при перегрузке операций сохраняются количество аргументов, приоритеты операций и правила ассоциации (справа налево или слева направо), используемые в стандартных типах данных; для стандартных типов данных переопределять операции нельзя; функции-операции не могут иметь аргументов по умолчанию; функции-операции наследуются (за исключением =); функции-операции не могут определяться как static. Функцию-операцию можно определить тремя способами: как метод класса, как дружественную функцию класса или как обычную функцию. В двух последних случаях функция должна принимать хотя бы один аргумент, имеющий тип класса, указатели или ссылки на класс. Для некоторых операций есть ограничения. Функция-оператор содержит ключевое слово operator, за которым следует знак переопределяемой операции: тип operator операция ( список параметров) {тело функции} Унарные операции Перегрузку унарных операций иллюстрирует пример простого класса-счетчика, выполняющего циклический счет от нуля до заданного максимального значения. #include "stdafx.h" #include <iostream> #include <cmath> using namespace std; class Counter { int n=0; //Текущее значение счетчика int max_n=100; // максимальное значение счетчика public: // Конструктор--------------------------------------------------1 Counter (int max_n =100) {this->max_n=abs(max_n);} // получение текущго значения-------------------------------------------2 int get_n(){return n;} // префиксный инкремент как метод класса--------------------------------3 Counter& operator ++() { n = n <max_n? ++n:0; return *this; } // постфиксный инремент как метод класса -------------------------------4 Counter operator ++{int) { Counter temp (*this); n = n <max_n? ++n:0; return temp; } //префиксный декремент как дружественная функция-------------------5 friend Counter& operator --(Counter & c); // постфиксный декремент как дружественнаяфункция -----------------6 friend Counter operator --(Counter & c, int); // вывод в поток как дружественнаяфункция -------------------------7 friend ostream& operator << (ostream & out, Counter & c){ return out << c.n << " " << c.max_n<< endl; } }; Counter& operator --(Counter & c){ c.n = c.n >0? --c.n : c.max_n; return c; } Counter operator --(Counter & c, int){ Counter temp(c); c.n = c.n >0? --c.n : c.max_n; return temp; } int _main() { Counter c(10); ++c; c++; --c;//с переменой типа "счетчик" // стало проще работать cout << c<< endl; }