для реализации полиморфизма. Если у вас нет указателя базового класса, указывающего на производный объект, у вас не может быть здесь полиморфизма.
Одной из ключевых особенностей производных классов является то, что указатель на производный класс совместим по типу с указателем на его базовый класс. Полиморфизм — это искусство использования преимущества этой простой, но мощной и универсальной функции, которая раскрывает весь потенциал объектно-ориентированных методологий.
В C++ существует особое отношение типа/подтипа, при котором указатель базового класса или ссылка могут обращаться к любому из подтипов производного класса без вмешательства программиста. Эта возможность манипулировать более чем одним типом с помощью указателя или ссылки на базовый класс называется полиморфизмом.
Полиморфизм подтипов позволяет нам писать ядро нашего приложения независимо от отдельных типов, которыми мы хотим манипулировать. Вместо этого мы программируем общедоступный интерфейс базового класса нашей абстракции с помощью указателей и ссылок базового класса. Во время выполнения фактический тип, на который ссылаются, разрешается, и вызывается соответствующий экземпляр открытого интерфейса. Разрешение во время выполнения соответствующей вызываемой функции называется динамической привязкой (по умолчанию функции разрешаются статически во время компиляции). В C++ динамическое связывание поддерживается с помощью механизма, называемого виртуальными функциями класса. Полиморфизм подтипов посредством наследования и динамического связывания обеспечивает основу для объектно-ориентированного программирования.
Основное преимущество иерархии наследования заключается в том, что мы можем программировать для общедоступного интерфейса абстрактного базового класса, а не для отдельных типов, которые формируют его иерархию наследования, таким образом защищая наш код от изменений в этой иерархии. Например, мы определяем eval() как общедоступную виртуальную функцию абстрактного базового класса Query. Написание кода, такого как _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( ¬q );
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 объекта класса. В предыдущем примере 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* могут быть описаны как полиморфные, но они не имеют явной языковой поддержки, т. е. программист должен управлять ими посредством явного приведения типов и какой-либо формы дискриминанта, отслеживающего фактический адресуемый тип.
person
Rohit Vipin Mathews
schedule
24.02.2012