Во-первых: подчеркнём одну концепцию

Определение функции как виртуальной не означает, что функция не реализована.

Определение её как виртуальной позволяет использовать указатель на базовый класс для вызова этой функции производного класса.

Только определение функции как чисто виртуальной означает, что функция не реализована.

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

1. Введение

Предположим, у нас есть следующая иерархия классов:

Пример

class A { public:     virtual void foo()     {         cout<<"A::foo() вызвана"<<endl;     } }; class B:public A { public:     void foo()     {         cout<<"B::foo() вызвана"<<endl;     } }; int main(void) {     A *a = new B();     a->foo();   // Здесь, хотя a — указатель на A, вызываемая функция (foo) принадлежит B!     return 0; }

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

Виртуальные функции могут реализовать полиморфизм только с помощью указателей или ссылок.


Чистые виртуальные функции в C++

1. Определение

 

Чисто виртуальная функция — это виртуальная функция, объявленная в базовом классе. Она не имеет определения в базовом классе, но требует, чтобы любой производный класс определил собственный способ реализации. Способ реализации чисто виртуальной функции в базовом классе — добавить после прототипа функции=0:

 
virtual void funtion1()=0

2. Причины введения

  

1. Чтобы удобно использовать возможности полиморфизма, нам часто требуется определять виртуальные функции в базовом классе.

  

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

  

Чтобы решить вышеуказанные проблемы, вводится понятие чисто виртуальных функций, и функция определяется как чисто виртуальная функция (способ:virtual ReturnType Function()= 0;), тогда компилятор требует, чтобы она была переопределена в производных классах для реализации полиморфизма. В то же время класс, содержащий чисто виртуальные функции, называется абстрактным классом, который не может создавать объекты. Это хорошо решает две вышеуказанные проблемы.

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

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

Цель определения чисто виртуальных функций — заставить производные классы наследовать только интерфейс функции.

Значение чисто виртуальных функций состоит в том, чтобы позволить всем объектам класса (главным образом объектам производных классов) выполнять действия чисто виртуальной функции, но класс не может предоставить разумную реализацию по умолчанию для чисто виртуальной функции. Поэтому объявление чисто виртуальной функции в классе говорит разработчикам подклассов: «Вы должны предоставить реализацию чисто виртуальной функции, но я не знаю, как вы её реализуете».


Введение в абстрактные классы

Абстрактный класс — это особый класс, создаваемый для целей абстракции и проектирования. Он находится на более высоком уровне иерархии наследования.

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

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

(3) Примечания при использовании абстрактных классов:

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

Итог:

  • 1. Чисто виртуальная функция объявляется следующим образом:virtual void funtion1()=0;Чисто виртуальная функция обязательно не имеет определения. Чисто виртуальные функции используются для стандартизации поведения производных классов, то есть интерфейса. Класс, содержащий чисто виртуальные функции, является абстрактным классом. Абстрактный класс не может определять экземпляры, но можно объявлять указатели или ссылки на конкретные классы, реализующие абстрактный класс.

  • 2. Виртуальная функция объявляется следующим образом:virtual ReturnType FunctionName(Parameter)Виртуальная функция должна быть реализована. Если она не реализована, компилятор сообщит об ошибке, с сообщением об ошибке:

    error LNK****: unresolved external symbol "public: virtual void __thiscall ClassName::virtualFunctionName(void)"
  • 3. Для виртуальных функций и родительский, и дочерний класс имеют свои собственные версии. При вызове полиморфным способом они динамически связываются.

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

  • 5. Виртуальные функции — это механизм в C++, используемый для реализации полиморфизма. Основная идея — получать доступ к функциям, определённым в производных классах, через базовые классы.

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

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

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

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

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

На самом деле, по моему личному мнению, введение чисто виртуальных функций преследует две цели:

  • 1. Для безопасности: чтобы избежать неизвестных результатов, вызванных невнимательностью там, где требуется явная реализация, напоминая подклассам реализовать то, что они должны.
  • 2. Для эффективности: не эффективности выполнения программы, а эффективности написания кода.

Исходный URL: https://blog.csdn.net/hackbuteer1/article/details/7558868