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

Язык C++ / Виртуальные функции

Виртуальные функции — специальный вид функций-членов класса. Виртуальная функция отличается об обычной функции тем, что для обычной функции связывание вызова функции с ее определением осуществляется на этапе компиляции. Для виртуальных функций это происходит во время выполнения программы.

Для объявления виртуальной функции используется ключевое слово virtual. Функция-член класса может быть объявлена как виртуальная, если

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

Виртуальная функция — это функция, которая определяется в базовом классе, а любой порожденный класс может ее переопределить. Виртуальная функция вызывается только через указатель или ссылку на базовый класс.

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

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

#include <iostream>
using namespace std;
class X {
protected:
  int i;
public:
  void seti(int c) {i = c;}
  virtual void print() {cout << endl <<"class X: " << i;}
};
class Y : public X {
public:
  void print() {cout << endl << "class Y: " << i;}
};
int main() {
  X x;
  X *px=&x;  // Указатель на базовый класс
  Y y;
  x.seti(10);
  y.seti(15);
  px->print();  // класс X: 10
  px=&y;
  px->print();  // класс Y: 15
  cin.get();
  return 0;
}

Результат выполнения
Виртуальная функция
В каждом случае выполняется различная версия функции print(). Выбор динамически зависит от объекта, на который ссылается указатель.

В терминологии ООП «объект посылает сообщение print и выбирает свою собственную версию соответствующего метода». Виртуальной может быть только нестатическая функция-член класса. Для порожденного класса функция автоматически становится виртуальной, поэтому ключевое слово virtual можно опустить.

// Выбор виртуальной функции
#include <iostream>
using namespace std;
class figure {
protected:
  double x, y;
public:
  figure(double a=0, double b=0) {x = a; y = b;}
  virtual double area()   {return(0);} // по умолчанию
};
class rectangle : public figure {
public:
  rectangle(double a=0, double b=0) : figure(a, b) {};
  double area()  {return(x*y);}
};
class circle : public figure {
public:
  circle(double a=0) : figure(a, 0) {};
  double area()  {return(3.1415*x*x);}
};
int main() {
  figure *f[2];
  rectangle rect(3, 4);
  circle cir(2);
  double total = 0;
  f[0] = &rect;
  f[1] = &cir;
  total = f[1]->area();
  cout << total << endl;
  total += f[0]->area();
  cout << total << endl;
  cin.get();
  return 0;
}

Результат выполнения
Виртуальная функция

Абстрактные базовые классы

Базовый класс иерархии типа обычно содержит ряд виртуальных функций, которые обеспечивают динамическую типизацию. Часто в самом базовом классе сами виртуальные функции фиктивны и имеют пустое тело. Определенное значение им придается лишь в порожденных классах. Такие функции называются чистыми виртуальными функциями.

Чистая виртуальная функция — это функция-член класса, тело которой не определено.

В базовом классе такая функция записывается следующим образом:

 virtual ПрототипФункции = 0;

 Например

 virtual void func() = 0;

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

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

#include <iostream>
using namespace std;
class figure {
  protected:
    double x, y;
  public:
    figure(double a=0, double b=0) {x = a; y = b;}
    virtual double area()=0 // чистая виртуальная функция
};
class rectangle : public figure {
  public:
    rectangle(double a=0, double b=0) : figure(a, b) {};
    double area() {return(x*y);}
};
class circle : public figure {
  public:
    circle(double a=0) : figure(a, 0) {};
    double area() {return(3.1415*x*x);}
};
int main() {
  ...
}

Назад


Назад: Язык C++

Комментариев к записи: 2

  • Fruit Ninja Online

    Виртуальные методы — один из важнейших приёмов реализации полиморфизма . Они позволяют создавать общий код, который может работать как с объектами базового класса, так и с объектами любого его класса-наследника. При этом базовый класс определяет способ работы с объектами и любые его наследники могут предоставлять конкретную реализацию этого способа.


  • Сюда же можно было бы про виртуальный деструктор написать, я думаю.

    Статья годная, первый пример не очень удачный.

    С абстрактными классами можно было бы связать второй пример. У Вас там как раз описывается некая «фигура», т.е. тоже абстрактное понятие, для которого нельзя описать метод вычисления площади или чего-то там.
    Это был бы удачный пример, т.к. это:

    virtual double area() {return(0);} // по умолчанию

    не совсем верно.

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

    Термин «отсроченный метод» какой-то странный. Он встречается в классических книгах?


Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *