Виртуальные функции

Виртуальные функции. Абстрактные классы

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

class Detail
{
public:
virtual void print() const;
};

Реализация функции print в базовом классе может быть осуществлена следующим образом:

void Detail::print() const
{
cout<<"Атрибуты детали"<<endl;
cout<<m_p.x<<m_p.y<<m_p.z<<endl;
cout<<m_D<<endl;
}

Реализация функции print в классе-наследнике может быть осуществлена следующим образом:

class Lens: public Detail
{
public:
virtual void print() const;
};

void Lens::print() const // замещает виртуальную функцию базового класса
{
cout<<"Атрибуты линзы"<<endl;
cout<<m_media.n<<m_R1<<m_R2<<m_d<<endl;
}

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

void Lens::print() const // замещает виртуальную функцию базового класса
{
Detail::print();
cout<<"Атрибуты линзы"<<endl;
cout<<m_media.n<<m_R1<<m_R2<<m_d<<endl;
}

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

Делать виртуальными можно практически все функции за исключением конструкторов и оператора =. Особый интерес представляют виртуальные деструкторы.

class Shape
{
public:
Shape() { }
virtual ~Shape() { }
};

class Polygon: public Shape
{
private:
Point* m_points;
public:
Polygon(int n) { m_points=new Point[n]; }
~Polygon() { delete [] m_points; }
};

void main()
{
Shape* s=new Polygon();
...
delete s;
}

Как же класс Shape определит реальный объем памяти занимаемый классом? Это сделает сам наследник, который дополнит виртуальный деструктор ~Shape(), деструктором наследника ~Polygon(). Утечек памяти не возникнет.

Абстрактные классы

Некоторые базовые классы (Detail) представляют собой абстрактную концепцию, для которой не могут существовать экземпляры. Как нарисовать деталь? Как выполнить расчет прохождения луча через деталь? Невозможно. Чтобы не возникло ошибки целесообразно создать абстрактный класс, экземпляр которого создать нельзя, но можно создать экземпляры его наследников. Абстрактным называется класс имеющий чисто виртуальные функции.

Многие классы бывают очень полезны в качестве базовых, но бессыслены сами по себе. Некоторые базовые классы (Shape) представляют собой абстрактную концепцию, для которой не могут существовать экземпляры. Как нарисовать объект? Как выполнить расчет его площади? Невозможно. Чтобы не возникло ошибки целесообразно создать абстрактный класс, экземпляр которого создать нельзя, но можно создать экземпляры его наследников.

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

class Shape
{
public:
Point center;
Color foreground;

virtual double Area() = 0; //чисто виртуальная функция
virtual void Draw() = 0; //чисто виртуальная функция
};

class Rectangle: public Shape
{
public:
double m_height, m_width;
double Area() { return m_height*m_width};
void Draw() {... };
};

class Circle: public Shape
{
public:
double m_radius;
double Area() { return 2.*acos(0.)*m_radius*m_radius };
void Draw() {... };
};

Таким образом, функции базового класса могут безопасно вызывать любые свои функции, в том числе и виртуальные. Ведь они будут перегружены в классах наследниках.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: