Статические поля применяются для хранения данных, общих для всех объектов класса, например, количества объектов или ссылки на разделяемый всеми объектами ресурс. Эти поля существуют для всех объектов класса в единственном экземпляре, то есть не дублируются.
Свойства статических полей:
1) память под статическое поле выделяется один раз при его инициализации независимо от числа созданных объектов (и даже при их отсутствии) и инициализируется с помощью операции доступа к области действия, а не операции выбора (определение должно быть записано вне функций):
#include <iostream.h>
Class Example
{public:
static int value;//объявление в классе
};
int Example::value; //определение статического поля в глобальной области, по умолчанию инициализируется нулем.
// int Example::value=10; //пример инициализации произвольным значением
2) статические поля доступны как через имя класса, так и через имя объекта
3) на статические поля распространяется действие спецификаторов доступа, поэтому статические поля, описанные как private, нельзя изменить с помощью операции доступа к области действия; это можно сделать только с помощью статических методов;
4) память, занимаемая статическим полем, не учитывается при определении размера с помощью операции sizeof.
Статические методы
Статическая функция класса может обращаться только к статическим полям класса и вызывать только другие статические методы классы, так как им не передается скрытый указатель this. В то время как нестатические функции класса должны вызываться только через объекты класса, то статические функции такого ограничения не имеют. Статические функции вызывается или через имя класса, или через имя объекта.
#include <iostream.h>
Class Simple
{public:
static int sum(int v1, int v2) {return v2+v1;}
};
Void main()
{cout<<Simple::sum(10,20)<<"\n"; //первый способ вызова функции
Simple s1;
cout<<s1.sum(10,20); //второй способ вызова функции
}
Дружественные классы
Дружественность – возможность использования метода двумя и более объектами различных классов, связанных отношениями общности.
Дружественные классы необходимы в том случае, если не связанным отношением родства классам необходим доступ к закрытым или защищенным секциям одного из них.
#include <iostream.h>
//не правильный вариант
class A
{double x;
public:
A() {x=3.14;}
};
class System: public A
{public:
void f() {cout<<x;} //доступ к переменной x закрыт
};
//правильный вариант
class A
{ friend class System;
double x;
public:
A() {x=3.14;}
};
class System
{public:
A obj;
void f() {cout<<obj.x<<endl;}
};
Свойства друзей:
1) отношение дружественности не наследуются, то есть, если A дружественен B, а C порожден от A, то это не означает, что C становится автоматически дружественным B;
2) свойство дружественности не транзитивно, то есть, если класс А дружественен классу В, а класс В – классу С, то А не становится автоматически дружественным классу С;
3) свойство дружественности не коммутативно, то есть, если A дружественен B, то это не означает, что B дружественен A.
Но при этом A можно объявить дружественным B.
Взаимодружественные классы
class A; //неполное объявление класса
class B
{ friend class A;
public:
void f(A* c1) {};};
class A;
{ friend class B;};
Раздел, в котором помещено объявление friend, не имеет значения. Обычно для наглядности объявление friend для класса помещается в первой секции. Когда дружественной объявляется функция, объявление обычно помещается там, где была бы записана эквивалентная функция-член. Единственное ограничение, налагаемое на объявление friend, заключается в том, что это объявление должно находиться внутри объявления класса.
Контейнерные классы
Контейнерные классы – это классы, которые содержат в своем описании один или несколько объектов или указатели на объекты. В этом случае имеет место отношение «содержит».
Сначала инициализируются все поля–объекты, которые содержатся в описании класса, причем в том порядке, в котором они объявлены. Деструкторы вызываются в порядке, обратном инициализации.
Динамическое связывание в ООП (полиморфизм)
Полиморфизм – это свойство ООП, при котором одно и тоже сообщение может вызывать различные действия на этапе выполнения.
Полиморфизм – это характеристика функций-членов, а не объектов. Несмотря на то, что полиморфизм реализуется через архитектуру класса, полиморфными могут быть только функции-члены класса, а не весь класс. В С++ полиморфная функция привязывается к одной из возможных функций только тогда, когда ей передается конкретный объект, т. е. в С++ вызов функции в исходном коде только обозначается, без точного указания на то, какая именно функция вызывается. Этот процесс называется позднее связывание. Процесс, при котором компилятор (как в традиционных языках), базируясь на исходном коде, вызывает фиксированные идентификаторы функций, а компоновщик заменяет эти идентификаторы физическими адресами, называется раним связыванием.То есть идентификаторы функций ассоциируются с физическими адресами до этапа выполнения, еще на стадии компиляции и компоновки. При раннем связывании программы выполняются быстрее, но существенно ограничены возможности разработчика. При позднем связывании остро встает вопрос об эффективности исполняемой программы.
Способность объектно-ориентированных языков автоматически определять тип объекта на этапе выполнения программы называется RTTI (run-time type identification – идентификация во время выполнения).
Виртуальные функции
В С++ позднее связывание для функции определяется при ее объявлении с помощью ключевого слова virtual. Позднее связывание имеет смысл только для объектов, являющихся частью иерархии классов. Объявление функции виртуальной для класса (не используемого в качестве базового) синтаксически корректно, но приведет только к потере времени в момент выполнения.
Виртуальные функции – функции, вызов которых зависит от типа объектов. С помощью виртуальных функций объект определяет свои действия.
Правило: указатель на базовый класс может ссылаться на объект этого класса или любого другого, производного от базового.
A* aobject; A
B* bobject; B
C* cobject; C
cobject=&аobject // так нельзя делать
aobject=&cobject.
Пример:
//f1.h
#include <iostream.h>
class Animal
{public:
/*virtual*/ char* speak() {return "";}
};
class Dog: public Animal
{public:
char * speak() {return "Gav!!!";}
};
//f1.cpp
#include "f1.h"
void sound(Animal& i)
{cout<<i.speak()<<endl;}
void main()
{ Dog Sharic;
sound(Sharic); //”” (на экран будет выведена пустая строка)
}
Решение проблемы – позднее связывание. Функцию speak() класса Animal достаточно объявить виртуальной, после чего компилятор запустит механизмы позднего связывания.
//f1.h
#include <iostream.h>
#include <string.h>
class Animal
{
protected:
char *pname;
public:
Animal(char *AnName)
{
pname=new char[strlen(AnName)+1];
strcpy(pname, AnName);
}
virtual char* speak() {return "";}
virtual char *name() {return pname;}
};
class Dog: public Animal
{
public:
Dog(char *name):Animal(name) {}
char *speak()
{
char *phrase;
phrase=strdup(pname); //дублирует строку, при этом вызывая функцию malloc()