класса TValue. А нам необходимо, чтобы C++ вызывал функцию-член GetValue()
производного объекта, на который в действительности ссылается указатель. 
    Устранить это серьезное затруднение можно, объявив GetVaд() виртуальной 
функцией-членом. Вызов виртуальных функций-членов компонуются во время 
выполнения программы (с помощью техники, известной под названием позднее 
связывание). Вызовы обычных функций-членов компонуются во время компиляции
(раннее связывание). Виртуальные функции-члены и позднее связывание дают 
объектам возможность определять свое поведение во время выполнения. 

    ЗАМЕЧАНИЕ 

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

    В листинге 14.4 объявляются классы TValue и TMult, как это было описано 
ранее, но с одним отличием. В новых классах GetValue() объявлена виртуальной 
функцией-членом. Вы можете скомпилировать и залустить программу как DOS 
или EasyWin-приложение. 

    Листинг 14.4.  VIRTUAL,CPP (виртуальные и невиртуальные функции-члены) 

   1.   #include <iostream.h> 
   2. 
   3:	class TValue { 
   4:	protected: 
   5:	   int value; 
   6:	public: 
   7:	   TValue(int n) { value = n; }                     
   8:	   virtual int GetValue(void) { return value; }
   9:	};                      
   10:
   11:	class TMult:   public TValue { 
   12:	protected:               
   13:	   int multiplier; 
   14:	public:
   15:	   TMult(int n,  int m): TValue(n) { multiplier = m;   } 
   16:	   virtual int *GetValue(vpid)  {  return value * multiplier;   } 
   17:	}; 
   18:
   19:	main()
   20:	{ 
   21:	   TValue -basep;
   22:
   23:	basep = /few TValue(10);   
   24:	cout « basep->GetValue() «  '\n';    // Выведет 10 
   25:	delete basep; 
   26:	 
   27:	basep = new TMult(10,  2);         .   // !!! 
   28:	cout « basep->GetValue() « '\n';    // !!! Выведет 20 
   29:	delete basep;
   30:	
   31:           return 0; 
   32:         }         

    В строках 8 и 16 объявлению функции GetValue() предшествует 
ключевое слово virtual. Это служит указанием компилятору, что вызов 
функции-члена GetValue() компонуется во время выполнения. В технической
реализации адреса двух виртуальных функций-членов GetValue()
сохраняются во внутренней таблице. Когд операторы вызывает виртуальную 
функцию-член, C++ ищет адрес нужной функции в этой таблице. Если вы полагаете, 
что эти поиски отнимают время, то вы совершенно правы. Использование 
виртуальных функци может отрицательно сказаться на быстродействии вашей 
программы, хотя на практике серьезные замедления нежелательны. 
    Виртуальные функции-члены наследуются производными классами, как и обычные 
функции-члены. Конечно, однажды объявленная виртуальной, функция-член не 
может измениться. Другими словами, для добавления нового кода в функции 
GetValue() эта функция-член должна объявляться в производном классе TMult
(строки 11-17) точно так же, как это было сделано в TValue (строки 3-9). 
Конечно, вы можете удалитьключевое слово virtual в последнем из производных 
классов в линии наследования. 
    Когда вы запустите VIRTUAL на выполнение, вы увидите на экране значения 10 
и 20 — результаты вызова двух виртуальных функций-членов GetValueO. В строке 
21 объявляется указатель basep на класс TValue. В строке 23 вызывается new для 
создания объекта TValue, который инициализируется значением 10 (строка 24), 


    354 Часть III. Программирование на ANSI C++ 
