Зачем использовать указатели базовых классов для производных классов - PullRequest
21 голосов
/ 24 февраля 2012
class base{
    .....
    virtual void function1();
    virtual void function2();
};

class derived::public base{
    int function1();
    int function2();
};

int main()
{
    derived d;
    base *b = &d;
    int k = b->function1() // Why use this instead of the following line?
    int k = d.function1(); // With this, the need for virtual functions is gone, right?

}

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

Ответы [ 4 ]

53 голосов
/ 24 февраля 2012

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

4 голосов
/ 24 февраля 2012

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

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

Вот самый простой пример использования виртуальных функций:

base *b = new derived;
b->function1();
delete b;
1 голос
/ 24 февраля 2012

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

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

В C ++ существует специальное отношение тип / подтип, в котором база указатель класса или ссылка может обращаться к любому из его производного класса подтипы без вмешательства программиста. Эта способность манипулировать более одного типа с указателем или ссылкой на базовый класс о котором говорят как о полиморфизме.

Подтип полиморфизма позволяет нам написать ядро ​​нашего приложения независимо от отдельных типов, которыми мы хотим манипулировать. Скорее мы запрограммировать публичный интерфейс базового класса нашей абстракции через указатели и ссылки на базовый класс. Во время выполнения фактическая тип, на который ссылаются, разрешается, и соответствующий экземпляр общедоступный интерфейс вызывается. Разрешение во время выполнения соответствующая функция для вызова называется динамическим связыванием (по умолчанию функции разрешаются статически во время компиляции). В C ++ динамический привязка поддерживается с помощью механизма, называемого виртуальным классом функции. Подтип полиморфизма через наследование и динамику переплет обеспечивает основу для запрограммированного программирования

Основным преимуществом иерархии наследования является то, что мы можем программировать к общедоступному интерфейсу абстрактного базового класса, а не к отдельные типы, которые формируют его иерархию наследования, таким образом экранирование нашего кода от изменений в этой иерархии. Определяем eval (), например, как общедоступная виртуальная функция абстрактной базы запросов учебный класс. Написание кода, такого как _rop->eval(); Пользовательский код защищен от разнообразия и изменчивости нашего языка запросов. Это не только позволяет для добавления, пересмотра, или удаление типов без необходимости изменения пользовательских программ, но освобождает поставщика нового типа запроса от необходимости перекодировать поведение или действия, общие для всех типов в самой иерархии. Это поддерживаются две особые характеристики наследования: полиморфизм и динамическое связывание. Когда мы говорим о полиморфизме в C ++, мы в первую очередь означает способность указателя или ссылку на базовый класс обратиться к любому из его производных классов. Например, если мы определим не являющаяся членом функции eval () следующим образом, // pquery может обращаться к любому из классы, полученные из Query void eval( const Query *pquery ) { pquery->eval(); } мы можем вызвать его легально, передавая адрес объекта любого из четыре типа запросов:

    int main() 
{ 
AndQuery aq;
 NotQuery notq; 
OrQuery *oq = new OrQuery; 
NameQuery nq( "Botticelli" ); // ok: each is derived from Query 
// compiler converts to base class automatically 
eval( &aq );
 eval( &notq ); 
eval( oq ); 
eval( &nq );
 } 

тогда как попытка вызвать eval () с адресом объекта, не полученного из Query приводит к ошибке времени компиляции:

int main()
 { string name("Scooby-Doo" ); // error: string is not derived from Query 
eval( &name); 
}

Внутри eval () выполнение pquery-> eval (); должен ссылаться на соответствующая виртуальная функция-член eval (), основанная на фактическом классе адреса объектных запросов. В предыдущем примере pquery по очереди обращается к объекту AndQuery, объекту NotQuery, объекту OrQuery, и объект NameQuery. В каждой точке вызова во время выполнения нашей программы, фактический тип класса, к которому обращается pquery определяется и вызывается соответствующий экземпляр eval (). динамический привязка - это механизм, с помощью которого это достигается. В объектно-ориентированной парадигме программист манипулирует неизвестным экземпляром связанного, но бесконечного набора типов. (Набор Типы связаны его иерархией наследования. В теории, однако, есть нет предела глубине и ширине этой иерархии.) В C ++ это достигается путем манипулирования объектами через базовый класс только указатели и ссылки. В объектно-ориентированной парадигме программистманипулирует экземпляром фиксированного единственного типа, который полностью определен в точке компиляции.Хотя полиморфное манипулирование объектом требует доступа к объекту либо через указатель, либо через ссылку, манипулирование указателем или ссылкой в ​​C ++ само по себе не обязательно приводит к полиморфизму.Например, рассмотрим

// no polymorphism 
  int *pi; 
// no language-supported polymorphism 
  void *pvi; 
// ok: pquery may address any Query derivation
  Query *pquery;

В C ++ полиморфизм существует только в отдельных иерархиях классов.Указатели типа void * могут быть описаны как полиморфные, но они не имеют явной поддержки языка - то есть они должны управляться программистом посредством явных приведений и некоторой формы дискриминанта, которая отслеживает фактический адресуемый тип.

0 голосов
/ 24 февраля 2012

Вы, кажется, задали два вопроса (в заголовке и в конце):

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

  2. Зачем использовать виртуальные деструкторы, если мы можем избежать указателей на базовый класс? Проблема в том, что вы не всегда можете избежать указателей базового класса , чтобы использовать силу полиморфизма.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...