Опубликован: 25.08.2010 | Доступ: свободный | Студентов: 6099 / 598 | Оценка: 3.91 / 3.44 | Длительность: 11:09:00
Лекция 5:

Классы

< Лекция 4 || Лекция 5: 1234 || Лекция 6 >

Статические элементы класса

С помощью модификатора static можно описать статические поля и методы класса.

Статические поля

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

Память под статическое поле выделяется один раз при его инициализации независимо от числа созданных объектов (и даже при их отсутствии) и инициализируется с помощью операции доступа к области действия, а не операции выбора:

class A
{
public:
  static int count;
}
...
 A::count = 0;

Статические поля доступны как через имя класса, так и через имя объекта:

/* будет выведено одно и то же */
A *a, b; * cout << A::count << a->count << b.count;

На статические поля распространяется действие спецификаторов доступа, поэтому статические поля, описанные как private, нельзя инициализировать с помощью операции доступа к области действия, как описано выше. Им можно присвоить значения только с помощью статических методов, как описано ниже.

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

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

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

Статические методы могут обращаться непосредственно только к статическим полям и вызывать только другие статические методы класса, поскольку им не передается скрытый указатель this. Обращение к статическим методам производится так же, как к статическим полям - либо через имя класса, либо, если хотя бы один объект класса уже создан, через имя объекта.

Статические методы не могут быть константными ( const ) и виртуальными ( virtual ).

Дружественные функции и классы

Иногда желательно иметь непосредственный доступ извне к скрытым полям класса, то есть расширить интерфейс класса. Для этого служат дружественные функции и дружественные классы.

Дружественная функция

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

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

В качестве примера ниже приведено описание двух функций, дружественных классу monster. Функция kill является методом класса hero, а функция steal_ammo не принадлежит ни одному классу. Обеим функциям в качестве параметра передается ссылка на объект класса monster.

class monster;	// Предварительное объявление класса
class hero
{
... 
void kill(monster &);
};
class monster
{
...
friend int steal_ammo(monster &);
/* Класс hero должен быть определен ранее */
friend void hero::kill(monster &);	
};
int steal_ammo(monster &M){return --M.ammo;}
void hero::kill(monster &M){M.health = 0; M.ammo = 0;}

Дружественный класс

Если все методы какого-либо класса должны иметь доступ к скрытым полям другого, весь класс объявляется дружественным с помощью ключевого слова friend. В приведенном ниже примере класс mistress объявляется дружественным классу hero:

class hero{
	... friend class mistress;}
class mistress{
	... void f1();
	void f2();}

Функции f1 и f2 являются дружественными по отношению к классу hero (хотя и описаны без ключевого слова friend ) и имеют доступ ко всем его полям.

Объявление friend не является спецификатором доступа и не наследуется. Обратите внимание на то, что класс сам определяет, какие функции и классы являются дружественными, а какие нет.

Деструкторы

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

  • для локальных переменных - при выходе из блока, в котором они объявлены;
  • для глобальных - как часть процедуры выхода из main ;
  • для объектов, заданных через указатели, деструктор вызывается неявно при использовании операции delete (автоматический вызов деструктора при выходе указателя из области действия не производится).

При уничтожении массива деструктор вызывается для каждого элемента удаляемого массива. Для динамических объектов деструктор вызывается при уничтожении объекта операцией delete. При выполнении операции delete[] деструктор вызывается для каждого элемента удаляемого массива.

Имя деструктора начинается с тильды (~), непосредственно за которой следует имя класса. Деструктор:

  • не имеет аргументов и возвращаемого значения;
  • не может быть объявлен как const или static ;
  • не наследуется;
  • может быть виртуальным;
  • может вызываться явным образом путем указания полностью уточненного имени; это необходимо для объектов, которым с помощью new выделялся конкретный адрес.

Если деструктор явным образом не определен, компилятор создает его автоматически.

Описывать в классе деструктор явным образом требуется в случае, когда объект содержит указатели на память, выделяемую динамически - иначе при уничтожении объекта память, на которую ссылались его поля-указатели, не будет помечена как свободная. Указатель на деструктор определить нельзя.

Деструктор для рассматриваемого примера должен выглядеть так :

monster::~monster() {delete [] name;}

Перегрузка операций

С++ позволяет переопределить действие большинства операций так, чтобы при использовании с объектами конкретного класса они выполняли заданные функции. Эта дает возможность использовать собственные типы данных точно так же, как стандартные. Обозначения собственных операций вводить нельзя. Можно перегружать любые операции, существующие в С++, за исключением:

Таблица 5.1.
. .* ?: :: # ## sizeof

Перегрузка операций осуществляется с помощью функций специального вида ( функций-операций ) и подчиняется следующим правилам:

  • сохраняются количество аргументов, приоритеты операций и правила ассоциации (справа налево или слева направо) по сравнению с использованием в стандартных типах данных;
  • нельзя переопределить операцию по отношению к стандартным типам данных;
  • функция-операция не может иметь аргументов по умолчанию;
  • функции-операции наследуются (за исключением =);

Функцию-операцию можно определить тремя способами: она должна быть либо методом класса, либо дружественной функцией класса, либо обычной функцией. В двух последних случаях функция должна принимать хотя бы один аргумент, имеющий тип класса, указателя или ссылки на класс (особый случай: функция-операция, первый параметр которой - стандартного типа, не может определяться как метод класса).

Функция-операция содержит ключевое слово operator, за которым следует знак переопределяемой операции:

тип operator операция ( список параметров) { тело функции }

Перегрузка унарных операций

Унарная функция-операция, определяемая внутри класса, должна быть представлена с помощью нестатического метода без параметров, при этом операндом является вызвавший ее объект, например:

class monster
{...	monster & operator ++() {++health; return *this;}}
monster Vasia;
cout << (++Vasia).get_health();

Если функция определяется вне класса, она должна иметь один параметр типа класса:

class monster
{... friend	monster & operator ++( monster &M);};
monster& operator ++(monster &M) {++M.health; return M;}

Если не описывать функцию внутри класса как дружественную, нужно учитывать доступность изменяемых полей (в данном случае поле health недоступно извне, так как описано со спецификатором private, поэтому для его изменения требуется использование соответствующего метода, не описанного в приведенном примере).

Операции постфиксного инкремента и декремента должны иметь первый параметр типа int. Он используется только для того, чтобы отличить их от префиксной формы:

class monster
{...	monster operator ++(int){monster M(*this); health++; return M;}};
monster Vasia;
cout << (Vasia++).get_health();

Перегрузка бинарных операций

Бинарная функция-операция, определяемая внутри класса, должна быть представлена с помощью нестатического метода с параметрами, при этом вызвавший ее объект считается первым операндом:

class monster
{	...
	bool operator >( const monster &M) 
	{ 
	if( health > M.get_health()) return true;
	return false;
	}
};

Если функция определяется вне класса, она должна иметь два параметра типа класса:

bool operator >(const monster &M1, const monster &M2)
  {	if( M1.get_health() > M2.get_health()) return true;
	return false;
  }

Перегрузка операции присваивания

Операция присваивания определена в любом классе по умолчанию как поэлементное копирование. Эта операция вызывается каждый раз, когда одному существующему объекту присваивается значение другого. Если класс содержит поля ссылок на динамически выделяемую память, необходимо определить собственную операцию присваивания. Чтобы сохранить семантику операции, операция-функция должна возвращать ссылку на объект, для которого она вызвана, и принимать в качестве параметра единственный аргумент - ссылку на присваиваемый объект:

monster& operator = (const monster &M)
{
if (&M == this) return *this;	// Проверка на самоприсваивание
if (name) delete [] name;
if (M.name)
  {
  name = new char [strlen(M.name) + 1];
  strcpy(name, M.name);
  }
  else name = 0;
health = M.health; ammo = M.ammo; skin = M.skin;
return *this;
}

Операцию присваивания можно определять только в теле класса. Она не наследуется.

Перегрузка операции приведения типа

Можно определить функции-операции, которые будут осуществлять преобразование класса к другому типу. Формат:

operator имя_нового_типа ();

Тип возвращаемого значения и параметры указывать не требуется. Можно определять виртуальные функции преобразования типа.

Пример:

monster::operator int(){return health;}
...
monster Vasia; cout << int(Vasia);
< Лекция 4 || Лекция 5: 1234 || Лекция 6 >
Dana Kanatkyzi
Dana Kanatkyzi
Здравствуйте.Помогите решить задачу минимум 4 чисел.Условие такое:"Напишите функцию int min (int a, int b, int c, int d) (C/C++)"находящую наименьшее из четырех данных чисел."Заранее спасибо!
Александр Мигунов
Александр Мигунов
Россия