Energy
education


Объектно-ориентированный язык программирования. Разработан в 1998—2001 годах группой инженеров под руководством Андерса Хейлсберга в компании Microsoft как язык разработки приложений для платформы Microsoft .NET Framework и впоследствии был стандартизирован как ECMA-334 и ISO/IEC 23270.
19. Полиморфизм
Слово полиморфизм означает иметь много форм. В объектно-ориентированном программировании полиморфизмом часто понимается как «один интерфейс, множество функций».
Полиморфизм может быть статическим или динамическим. В статическом полиморфизме выбор определяется во время компиляции. В динамическом полиморфизме выбор определяется во время выполнения.
Статический полиморфизм
Механизм связывания функции с объектом во время компиляции называется ранним связыванием. Он также называется статическое связывание. C# использует два механизма для реализации статического полиморфизма. Это:
- Перегрузка метода
- Перегрузка оператора
Перегрузка метода
Вы можете иметь несколько определений для одного имени метода в одной области видимости. Определенные методы должны отличаться друг от друга по видам и/или количеству аргументов в списке аргументов. Нельзя перегрузить объявление методов, которые отличаются только типом возвращаемого значения.
Ниже приведен пример, в котором те же методы print() используются для печати различных типов данных:
using System; namespace PolymorphismApplication < class Printdata < void print(int i) < Console.WriteLine("Вывод int: ", i ); > void print(double f) < Console.WriteLine("Вывод float: " , f); > void print(string s) < Console.WriteLine("Вывод string: ", s); > static void Main(string[] args) < Printdata p = new Printdata(); // вызов метода print для вывода integer p.print(5); // вызов метода print для вывода float p.print(500.263); // вызов метода print для вывода string p.print("Привет C#"); Console.ReadKey(); >> >
Если приведенный выше код скомпилировать и выполнить, это приведет к следующему результату:
Вывод int: 5 Вывод float: 500.263 Вывод string: Привет C#
Динамический полиморфизм
C# позволяет создавать абстрактные классы, которые используются для обеспечения частичной реализации интерфейса. Реализация завершается, когда производный класс наследует от него. Абстрактные классы содержат абстрактные методы, которые осуществляются в производном классе. Производные классы имеют более специализированные функциональные возможности.
Обратите внимание на следующие правила об абстрактных классах:
- Вы не можете создать экземпляр абстрактного класса
- Вы не можете объявить абстрактный метод за пределами абстрактного класса
- Если класс объявлен как sealed, он не может быть унаследован, абстрактные классы не могут быть объявлены как sealed.
Следующая программа демонстрирует абстрактный класс:
using System; namespace PolymorphismApplication < abstract class Shape < public abstract int area(); >class Rectangle: Shape < private int length; private int width; public Rectangle( int a=0, int b=0) < length = a; width = b; >public override int area () < Console.WriteLine("Площадь прямоугольника :"); return (width * length); >> class RectangleTester < static void Main(string[] args) < Rectangle r = new Rectangle(10, 7); double a = r.area(); Console.WriteLine("Площадь: ",a); Console.ReadKey(); > > >
Если приведенный выше код скомпилировать и выполнить, это приведет к следующему результату:
Площадь прямоугольника : Площадь: 70
Если у вас есть метод, определенный в классе, который вы хотите реализовать в унаследованном классе, вы используете виртуальный метод. Виртуальные методы могут быть реализованы по-разному в разных унаследованных классах и вызов этих методов будет решаться во время выполнения.
Динамический полиморфизм реализуется абстрактными классами и виртуальными методами.
Следующая программа демонстрирует это:
using System; namespace PolymorphismApplication < class Shape < protected int width, height; public Shape( int a=0, int b=0) < width = a; height = b; >public virtual int area() < Console.WriteLine("Площадь родительского класса :"); return 0; >> class Rectangle: Shape < public Rectangle( int a=0, int b=0): base(a, b) < >public override int area () < Console.WriteLine("Площадь класса Rectangle :"); return (width * height); >> class Triangle: Shape < public Triangle(int a = 0, int b = 0): base(a, b) < >public override int area() < Console.WriteLine("Площадь класса Triangle :"); return (width * height / 2); >> class Caller < public void CallArea(Shape sh) < int a; a = sh.area(); Console.WriteLine("Площадь: ", a); > > class Tester < static void Main(string[] args) < Caller c = new Caller(); Rectangle r = new Rectangle(10, 7); Triangle t = new Triangle(10, 5); c.CallArea(r); c.CallArea(t); Console.ReadKey(); >> >
Если приведенный выше код скомпилировать и выполнить, это приведет к следующему результату:
Площадь класса Rectangle: Площадь: 70 Площадь класса Triangle: Площадь: 25
Администратор сайта: Колосов Михаил
email:
Copyright © 2011-2024. All rights reserved.
Полиморфизм: подавать холодным
Полиморфизм («истинный», параметрический) — свойство, позволяющее обрабатывать данные разных типов одним образом.
Полиморфизм является одним из трёх столпов ООП, наравне с наследованием и инкапсуляцией, да и в целом краеугольным камнем современного программирования. Думаю, большинство читателей не представляет своей жизни без полиморфизма, за что я, конечно, это большинство никак не осуждаю, ибо сам к нему принадлежу. Дело, однако, в том, что многие не задумываются об устройстве этого полиморфизма, ведь любой принцип программирования, по сути, представляет из себя математическую матрёшку.
Реализуем функции print_static , print_dynamic и print_enum для демонстрации различных методов реализации полиморфизма.
Статический полиморфизм
Грубо говоря, статическим называется такой полиморфизм, который не оказывает влияния на работу функции во время выполнения, но реализуется во время компиляции, следовательно, статический полиморфизм наиболее эффективен в отношении производительности.
Статическая диспетчеризация
Статическая диспетчеризация — единственный способ реализации статического полиморфизма (перегрузку, напомню, мы истинным полиморфизмом не считаем). Суть этого механизма заключается преимущественно в использовании т. н. типовых параметров, которые часто также называют дженериками (что, впрочем, не совсем верно, ведь типовые параметры — подкатегория дженериков). В некоторых языках существуют несколько примитивные реализации дженериков, называемые шаблонами. В основе работы дженериков лежит принцип мономорфизации — процесса генерации нескольких инстанций функции для разных типовых параметров (мономорфизация также сопровождается манглингом символов, т. е. спутыванием имён в бинаре, во избежание символьных конфликтов при линковке). Зачастую дженериками могут называть вещи, дженериками не являющиеся.
Rust
В Rust статическая диспетчеризация реализуется с помощью типовых параметров или ключевого слова impl (по сути, являющего собой анонимные типовые параметры). Напишем функцию print_static :
fn print_static(to: T) < println!(""); > print_static(123); print_static("abc")
Типовый параметр объявляется внутри <> , названием его служит T , а Display — трейт, позволяющий форматировать значения. В Rust вы не сможете отформатировать значение с помощью <> , пока не укажете, что типовый параметр реализует Display .
C++
В C++ статическая диспетчеризация реализуется с помощью шаблонов, декларируемых с помощью ключевого слова template . Функция:
template auto print_static(T to) -> void < std::cout print_static(123); print_static("abc");
Типовый параметр T объявлен с помощью ключевого слова typename , обозначающего произвольный тип. В C++ вам не нужно ограничивать область типов для функции, ошибку вы получите только в случае передачи аргумента типа, который нельзя записать в std::ostream . Стоит отметить, что в данном случае можно было обойтись без шаблона, вместо типового параметра T использовав auto .
Go
Go не поддерживает статический полиморфизм. В этом языке есть концепт, называемый типовыми параметрами, но на деле ими не являющийся, который я упомяну позже.
Динамический полиморфизм
Суть динамического полиморфизма заключается в определении типов и их поведения во время выполнения. Это позволяет сделать полиморфные функции более гибкими, однако часто бьёт по производительности.
Динамическая диспетчеризация
Динамическая диспетчеризация реализуется как передача вместе со значением т. н. виртуальной таблицы, хранящей информацию о типе и нужных для работы с ним методов.
Rust
В Rust динамическая диспетчеризация реализуется с помощью ключевого слова dyn . Динамические типы считаются безразмерными, потому их нужно передавать посредством указателя, ссылки или умного указателя (всё далее — указатель). Указатели на динамические типы являются толстыми, на деле представляющими из себя два указателя - на значение и на динамическую таблицу.
fn print_dynamic(to: &dyn Display) < println!(""); > print_dynamic(&123); print_dynamic(&"abc");
В данном случае мы передаём динамическое значение по ссылке, потому при сообщении аргумента требуется добавить амперсанд.
C++
В C++ не существует идентичного dyn Display механизма, однако сам этот язык гораздо более приспособлен к использованию динамической диспетчеризации, так как является объектно‑ориентированным, потому мы реализуем такой механизм с помощью наследования и ключевых слов virtual и override :
class Display < public: virtual auto writeln(std::ostream& to) ->void <>; >; class DisplayInt: public Display < public: int self; DisplayInt(int val) < self = val; >; auto writeln(std::ostream& to) -> void override < to self ; >; class DisplayStr: public Display < public: char* self; DisplayStr(char* val) < self = val; >; auto writeln(std::ostream& to) -> void override < to self ; >; auto display(int val) < return DisplayInt(val); >auto display(char* val) < return DisplayStr(val); >auto print_dynamic(Display&& to) -> void < to.writeln(std::cout); >print_dynamic(display(123)); print_dynamic(display("abc"));
В данном случае мы реализуем метод writeln для наследников базового класса Display . Переопределяемая display сугубо для схожести инициализации.
Go
В Go за динамическую диспетчеризацию отвечает механизм интерфейсов, их и используем:
func print_dynamic(to any) < fmt.Println(to) >print_dynamic(123); print_dynamic("abc");
В данном случае Go не требует наложения дополнительных ограничений на принимаемый тип, потому мы можем использовать интерфейс any , являющийся менее строгим аналогом any в C++ и dyn Any в Rust.
"Энамная" диспетчеризация
Энамная диспетчеризация — способ реализации динамического полиморфизма, характеризующийся передачей тэга, зачастую называемого дискриминантом, и юниона возможных типов. Этот метод превосходит динамическую диспетчеризацию в производительности, но уступает ей в гибкости, уступает также статической диспетчеризации в производительности, но превосходит её в гибкости. Словом, позволяет избежать некоторых недостатков динамической диспетчеризации там, где нельзя использовать статическую.
Rust
В Rust нет встроенного механизма для реализации энамной диспетчеризации, однако существует замечательный крейт, предоставляющий подобный механизм — enum_dispatch. Пользоваться сторонними библиотеками мы, однако, конечно же не будем, потому реализуем тип Displayable сами:
enum Displayable < Str(&'static str), I32(i32), >impl From for Displayable < fn from(value: i32) ->Self < Self::I32(value) >> impl From for Displayable < fn from(value: &'static str) ->Self < Self::Str(value) >> impl Display for Displayable < fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result < match self < Self::Str(s) =>write!(f, ""), Self::I32(i) => write!(f, "") > > > fn print_enum(to: Displayable) < println!(""); > print_enum(123.into()); print_enum("abc".into());
На строках 1-4 реализуется сам тип, на строках 6-16 — конверсия в него (используется на строках 31 и 32 путём вызова метода into ), на строках 18-25 — возможность форматирования типа. В функции мы без проблем принимаем готовую инстанцию.
C++
Как и Rust, C++ не предоставляет встроенной энамной диспетчеризации, потому реализуем сами:
struct Displayable < enum < STR, I32 >tag; union < char* str; int i32; >; Displayable(int val) < tag = I32; i32 = val; >; Displayable(char* val) < tag = STR; str = val; >; auto writeln(std::ostream& to) -> void < switch (this->tag) < case STR: to str << std::endl; break; case I32: to i32 > >; auto print_enum(Displayable to) -> void < to.writeln(std::cout); >print_enum(Displayable(123)); print_enum(Displayable("abc"));
И так, на строках 1-9 мы описываем сам тип, на 11-19 — конверсию, а на 21-30 — метод writeln , идентичный методу fmt в версии на Rust. В функции print_enum мы также принимаем готовую инстанцию. Стоит отметить, что похожего эффекта можно добиться при помощи std::variant .
Go
В Go существует встроенный механизм реализации энамной диспетчеризации — т. н. словари, которые часто неверно называют дженериками даже сами разработчики языка (не делайте так, пожалуйста). Поглядим на них в деле:
type Displayable interface < int | string >func print_enum[T Displayable](to T) < fmt.Println(to) >print_enum(123); print_enum("abc");
Словарь требует интерфейса‑ограничителя, возможные типы для нашего интерфейса Displayable указаны на строке 2. В качестве ограничителя также может выступать интерфейс any , однако в данном случае я избежал его использования для более точного раскрытия этого механизма в сравнении с предыдущими вариантами и возможной оптимизации.
Стоит упомянуть - C
В стандарте C11 в систему препроцессорных констант ( #define ) была встроена директива _Generic , позволяющая вручную осуществлять статическую диспетчеризацию.
#define print_static(VAL) _Generic((VAL), int: print_int, default: print_str)(VAL) print_int(int val) < printf("%d\n", val); >print_str(char* val) < puts(val); >print_static(123); print_static("abc");
Механизм действительно интересен и прогрессивен для си, хоть и не является полноценной реализацией типовых параметров. С другой стороны, местами такая реализация может быть даже более гибкой.
Заключения
Полиморфизм — воистину полиморфная вещь, различных подходов к реализации которой существует великое множество. Безусловно, без него мы бы не смогли писать код так, как можем сейчас.
Полиморфизм
Полиморфизм (polymorphism) — это понятие из объектно-ориентированного программирования, которое позволяет разным сущностям выполнять одни и те же действия. При этом неважно, как эти сущности устроены внутри и чем они различаются.
С греческого языка слово «полиморфизм» означает «многообразие». Термин используют и в других сферах. Скажем, полиморфизм в биологии — способность какого-то вида существовать в разных формах.
Пример полиформности в природе — пчелы: есть рабочие пчелы, матка, трутни. Они разные, но все могут летать независимо от того, что это за пчела. По похожему принципу работает и полиморфизм в ООП.

Освойте профессию
«Fullstack-разработчик на Python»
Профессия / 12 месяцев
Fullstack-разработчик на Python
Создавайте веб-проекты самостоятельно

Fullstack-разработчик на Python
Fullstack-разработчики могут в одиночку сделать IT-проект от архитектуры до интерфейса. Их навыки востребованы у работодателей, особенно в стартапах. Научитесь программировать на Python и JavaScript и создавайте сервисы с нуля.

Например, есть две разных сущности: картинка и видео. И тем, и другим можно поделиться: отправить в личное сообщение другому человеку. Программист может сделать два разных метода — один для картинки, другой для видео. А может воспользоваться полиморфизмом и создать один метод «Отправить» для обеих сущностей.
Такой метод будет называться полиморфным. Плюс этого подхода — разработчик пишет меньше кода и не повторяется.
Чтобы лучше понять, что такое полиморфизм и как он работает, советуем прочитать статью про ООП. Полиморфизм — один из четырех основных принципов этого способа программирования
Для чего нужен полиморфизм
- Облегчает написание кода. Не нужно придумывать десять разных методов: отправить одно, другое, третье. Есть один метод, который можно применять к разным сущностям и не задумываться.
- Позволяет масштабировать решения. Если понадобится отправлять не только видео и картинки, но и текст, это можно будет сделать той же командой.
- Делает код более читаемым. Разработчику не нужно разбираться, что делает десяток методов с похожими названиями. Есть один метод, и по его названию все понятно.
- Помогает точно понимать, чего ждать от разных методов, то есть делает код более предсказуемым. Не может быть такого, что метод «Отправить» вдруг окажется методом не для картинки, а для видео.
С понятием должен быть знаком любой разработчик. Множество языков программирования используют полиморфизм: C, C++, Java, Python и другие. Не все эти языки чисто объектно-ориентированные: некоторые устроены так, что с ними можно работать в разных парадигмах программирования. Так что столкнуться с полиморфизмом может каждый.

Станьте Fullstack-разработчик на Python и найдите стабильную работу
на удаленке
Полиморфизм как принцип ООП
В объектно-ориентированном программировании четыре основных принципа: инкапсуляция, абстракция, наследование и полиморфизм. Это связанные между собой вещи — без одного не работало бы и другое.
Инкапсуляция — это создание сущностей как «вещей в себе». Классы должны работать независимо друг от друга: если какая-то сущность, например, удалится, это не повлияет на принцип работы остальных.
Абстракция — это принцип, когда какие-то общие вещи сводятся до набора абстрактных признаков. То есть мы имеем не абсолютно разные классы «картинка», «видео», «текст», а абстрактный класс «контент».
Наследование — это возможность делать на основе одних сущностей другие. Обычно в качестве «родителя» используются абстрактные сущности, а от них наследуются уже более конкретные. То есть если родитель — «контент», то дети — «картинка», «видео», «текст». Это все подвиды контента и его наследники.
Чтобы реализовать полиморфизм, нужны как минимум абстракция и наследование. Они помогают сделать абстрактный класс, в нем — абстрактный «общий» метод, а потом унаследовать разные реализации этого метода. В итоге название одно, а механика разная в зависимости от подтипа. Сейчас разберемся подробнее.
Что такое полиморфный метод и как его создают
Полиформный — это многообразный: формы различаются, а действие одно и то же. Тот же самый метод «Отправить» из примера выше может быть реализован по-разному для видео и картинки. Но вызывается он одинаково для всех видов контента и выполняет одну и ту же задачу. Вот как это работает.
Сначала программист создает общий класс. Например, «контент». В нем он описывает вещи, общие для всего контента: свойства и методы. Свойства — это признаки, то, что есть у контента: количество лайков, возможность комментирования и так далее. А методы — это действия, то есть команды: контент можно лайкнуть, открыть на отдельной вкладке, репостнуть или отправить в личное сообщение.
У общего класса — абстрактные, общие методы. Контент можно отправить, но как — пока непонятно. Зато уже можно описать, как будет выглядеть эта команда: как она называется, что в нее нужно передать, какое сообщение выдать после этого. Это своего рода каркас будущего конкретного действия.
Затем разработчик создает производные классы. Это наследники общего класса: они более конкретные, у них могут быть дополнительные свойства и методы. Например, видео можно поставить на паузу, а картинку — нет. А еще у них сохраняются абстрактные свойства и методы родителя: их переопределяют, чтобы они тоже работали конкретнее.
У производных классов — свои реализации общих методов. Например, в классе «контент» есть метод «отправить». Он же есть и в производных классах. В классе «картинка» может быть свой код для отправки, а в классе «видео» — свой. Они могут быть похожи или различаться. Но название у них окажется одинаковым, и делать они будут одно и то же.
Можно создавать объекты производных классов и пользоваться их методами. Абстрактные классы существуют как бы в вакууме: нельзя создать объект, который будет принадлежать такому классу. Среди реальных объектов не может быть «просто контента», который не является ни текстом, ни картинкой, ни видео, ни еще чем-то. Соответственно, абстрактные методы никто не будет вызывать. А вот переопределенные методы из производных классов — вполне реальные, ими можно пользоваться в разных ситуациях.
Формы полиморфизма
Существуют разные виды полиморфизма. Вообще-то классификация довольно широкая и начинающему легко в ней запутаться, поэтому мы решили ограничиться только основными формами.
Полиморфизм подтипов. Это полиморфность «по умолчанию»: когда в ООП говорят о полиморфизме, обычно имеют в виду его. Выше мы рассказывали именно про такой тип. Это возможность использовать одни и те же команды, или интерфейсы, для разных сущностей — подтипов.
Параметрический полиморфизм. Его еще называют обобщенным полиморфизмом. В нем для команды не имеет значения, какую сущность ей прислали: для всех возможных классов будет использоваться один код. Такой полиморфизм считается «истинным» и делает код универсальнее, но реализовать его сложнее.
Полиморфизм ad hoc. Этот вид полиморфизма еще называют специализированным. Его иногда противопоставляют параметрическому: идея ad hoc — разный код при одинаковом названии. Часто такой полиморфизм реализуют с помощью перегрузки методов: несколько раз пишут метод с одним и тем же названием, но разным кодом.
Статический и динамический полиморфизм
На более «глубоком», близком к машине уровне полиморфизм можно разделить на две группы — статический и динамический. Разница —в том, когда программа переходит от общего метода к одной из его вариаций.
- Статический — метод переопределяют при компиляции.
- Динамический — при выполнении программы.
Статический полиморфизм реализуют с помощью перегрузки методов, о которой мы рассказывали выше. Динамический — с помощью абстракций. Обычно в объектно-ориентированных языках есть возможность применить оба варианта.
Преимущества полиморфизма
- Код становится аккуратнее: не приходится множить сущности и создавать десяток команд, которые делают одно и то же. Нет «лапши» — бессвязного неструктурированного кода, в котором тяжело разобраться.
- Разработчику удобнее: не нужно каждый раз думать, что делает команда «Отправить» для конкретного вида контента, можно просто написать ее и получить результат.
- Работать с разными сущностями можно одинаково: не обязательно каждый раз узнавать, о каком именно производном классе речь идет на этот раз. Ведь общее полиморфное действие есть для всего.
- Код легче расширять, использовать заново и всячески модифицировать.
Недостатки полиморфизма
- Связь полиморфизма с наследованием порой расценивают как слабое место всей концепции. Если нужен полиформный класс, но для конкретной ситуации не подходит наследование, — реализация может усложниться.
- Не всегда полиморфизм легко реализовать на практике. Поэтому существует довольно много реализаций, которые работают плохо: с багами и необъяснимыми ошибками.
- Полиморфизм может ухудшать производительность кода, делать его более «тяжелым» и медленным. Но тут многое зависит от реализации: скажем, параметрический обычно быстрее, чем ad hoc.
- Новичкам бывает тяжело понять принцип — объяснить полиморфизм простыми словами можно только в связке с другими понятиями ООП. Так что человек на момент изучения уже должен понимать, что такое объектно-ориентированное программирование.
Как начать изучать полиморфизм
Мы советуем начать с основ: сначала разобраться, как работает программирование в целом, потом перейти к принципам ООП и полиморфизму. На всех этапах лучше практиковаться, чтобы закреплять знания, — к тому же на реальных примерах легче понять тот или иной концепт.
Вы можете записаться на наши курсы и начать учиться уже сейчас. Обещаем много реальных задач и интересной практики!
64. Разумно сочетайте статический и динамический полиморфизм
Статический и динамический полиморфизм дополняют друг друга. Следует ясно представлять себе их преимущества и недостатки, чтобы использовать каждый из них там, где он дает наилучшие результаты, и сочетать их так, чтобы получить лучшее из обоих миров.
Динамический полиморфизм предстает перед нами в форме классов с виртуальными функциями и объектов, работа с которыми осуществляется косвенно — через указатели или ссылки. Статический полиморфизм включает шаблоны классов и функций.
Полиморфизм означает, что данное значение может иметь несколько типов, а данная функция может принимать аргументы типов, отличающихся от точных типов ее параметров. "Полиморфизм представляет собой способ получить немного свободы динамической проверки типов, не теряя преимуществ статической проверки" — [Webber03].
Сила полиморфизма состоит в том, что один и тот же фрагмент кода может работать с разными типами, даже с теми, которые не были известны в момент написания этого кода. Такая "применимость задним числом" является краеугольным камнем полиморфизма, поскольку существенно увеличивает пригодность и возможность повторного использования кода (см. рекомендацию 37). (В противоположность этому мономорфный код работает только со строго конкретными типами, теми, для работы с которыми он изначально создавался.)
Динамический полиморфизм позволяет значению иметь несколько типов посредством открытого наследования. Например, Derived* p можно рассматривать как указатель не только на Derived, но и на объект любого типа Base, который прямо или косвенно является базовым для Derived (свойство категоризации). Динамический полиморфизм известен также как включающий полиморфизм, поскольку множество, моделируемое Base, включает специализации, моделируемые Derived.
Благодаря своим характеристикам динамический полиморфизм в С++ наилучшим образом подходит для решения следующих задач.
• Единообразная работа, основанная на отношении надмножество/подмножество. Работа с различными классами, удовлетворяющими отношению надмножество/подмножество (базовый/производный), может выполняться единообразно. Функция, работающая с объектом Employee (Служащий), будет работать и с объектами Secretary (Секретарь).
• Статическая проверка типов. В С++ все типы проверяются статически.
• Динамическое связывание и раздельная компиляция. Код, который использует иерархию классов, может компилироваться отдельно от этой иерархии. Это становится возможным благодаря косвенности, обеспечиваемой указателями (как на объекты, так и на функции).
• Бинарная согласованность. Модули могут компоноваться как статически, так и динамически, до тех пор, пока схемы виртуальных таблиц подчиняются одним и тем же правилам.
Статический полиморфизм посредством шаблонов также позволяет значению иметь несколько типов. Внутри шаблона
t может иметь любой тип, который можно подставить в f для получения компилируемого кода. Это называется "неявным интерфейсом" в противоположность явному интерфейсу базового класса. Таким образом достигается та же цель полиморфизма — написание кода, который работает с разными типами — но совершенно иным путем.
Статический полиморфизм наилучшим образом подходит для решения следующих задач.
• Единообразная работа, основанная на синтаксическом и семантическом интерфейсе. Работа с типами, которые подчиняются синтаксическому и семантическому интерфейсу, может выполняться единообразно. Интерфейсы в данном случае представляют синтаксическую сущность и не основаны на сигнатурах, так что допустима подстановка любого типа, который удовлетворяет данному синтаксису. Например, пусть дана инструкция int i = p->f(5);. Если p — указатель на класс Base, эта инструкция вызывает определенную функцию интерфейса, вероятно, virtual int f(int). Но если p имеет обобщенный тип, то этот вызов может быть связан со множеством различных вещей, включая, например, вызов перегруженного оператора operator->, который возвращает тип, в котором определена функция X f(double), где X — тип, который может быть преобразован в int.
• Статическая проверка типов. Все типы проверяются статически.
• Статическое связывание (мешает раздельной компиляции). Все типы связываются статически.
• Эффективность. Вычисления во время компиляции и статическое связывание позволяют достичь оптимизации и эффективности, недоступных при динамическом связывании.
Определите ваши приоритеты и используйте каждый вид полиморфизма там, где проявляются его сильные стороны.
Следует сочетать статический и динамический полиморфизм для того, чтобы получить преимущества обоих видов полиморфизма, а не для того, чтобы комбинировать их недостатки.
• Статика помогает динамике. Используйте статический полиморфизм для реализации динамически полиморфных интерфейсов. Например, у вас может быть абстрактный базовый класс Command, и вы определяете различные реализации в виде шаблона
template* . */> class ConcreteCommand: public Command
В качестве примеров можно привести реализации шаблонов проектирования Command и Visitor (см. [Alexandrescu01] и [Sutter04]).
• Динамика помогает статике. Обобщенный, удобный, статически связываемый интерфейс может использовать внутреннюю динамическую диспетчеризацию, что позволяет обеспечить одинаковую схему размещения объектов. Хорошими примерами могут служить реализации размеченных объединений (см. [Alexandrescu02b] и [Boost]) и параметр Deleter у tr1::shared_ptr (см. [C++TR104]).
• Прочие сочетания. Плохим является сочетание, при котором комбинируются слабые стороны обоих видов полиморфизма и результат получается хуже, чем при их отдельном использовании. Правильное сочетание должно комбинировать лучшее от обоих видов полиморфизма. Например, не помещайте виртуальные функции в шаблон класса, если только вы не хотите, чтобы каждый раз инстанцировались все виртуальные функции (в противоположность невиртуальным функциям шаблонных типов). В результате вы можете получить астрономический размер кода и чрезмерно ограничить ваш обобщенный тип, инстанцируя функциональность, которая никогда не используется.
[Alexandrescu01] §10 • [Alexandrescu02b] • [C++TR104] • [Gamma95] • [Musser01] §1.2-3, §17 • [Stroustrup00] §24.4.1 • [Sutter00] §3 • [Sutter02] §1 • [Sutter04] §17, §35 • [Vandevoorde03] §14 • [Webber03] §8.6
Читайте также
Статический вес
Статический вес Определение страниц, получающих недостаточный статический вес. Часть важных страниц может недополучать статический вес. Вместо этого наибольший вес может переходить к непродвигаемым и техническим страницам. Чтобы решить эту проблему, необходимо:?
Объекты имеют статический тип
Объекты имеют статический тип Один из выводов, который можно сделать из трех требований QueryInterfасе , состоит в том, что множество интерфейсов, поддерживаемых объектом, не может изменяться во времени. Спецификация СОМ четко требует, чтобы этот вывод был верен для всех
10.1. PSPICE как статический логический анализатор
10.1. PSPICE как статический логический анализатор Шаг 1 Начертите в редакторе SCHEMATICS схему, изображенную на рис. 10.1. Необходимые компоненты вы найдете в библиотеке EVAL.slb. Редактор для установления метки (out) можно открыть, дважды щелкнув мышью по соответствующему участку
43. Разумно пользуйтесь идиомой Pimpl
43. Разумно пользуйтесь идиомой Pimpl РезюмеС++ делает закрытые члены недоступными, но не невидимыми. Там, где это оправдывается получаемыми преимуществами, следует подумать об истинной невидимости, достигаемой применением идиомы Pimpl (указателя на реализацию) для реализации
1.1.3. Полиморфизм
1.1.3. Полиморфизм Термин «полиморфизм», наверное, вызывает самые жаркие семантические споры. Каждый знает, что это такое, но все понимают его по-разному. (Не так давно вопрос «Что такое полиморфизм?» стал популярным во время собеседования при поступлении на работу. Если его
Полиморфизм
Полиморфизм Третьим принципом ООП является полиморфизм. Он характеризует способность языка одинаково интерпретировать родственные объекты. Эта особенность объектно-ориентированного языка позволяет базовому классу определить множество членов (формально называемых
Совет 12. Разумно оценивайте потоковую безопасность контейнеров STL
Совет 12. Разумно оценивайте потоковую безопасность контейнеров STL Мир стандартного С++ выглядит старомодным и не подверженным веяниям времени. В этом мире все исполняемые файлы компонуются статически, в нем нет ни файлов, отображаемых на память, ни общей памяти. В нем нет
17.5.3. Статический вызов виртуальной функции
17.5.3. Статический вызов виртуальной функции Вызывая виртуальную функцию с помощью оператора разрешения области видимости класса, мы отменяем механизм виртуализации и разрешаем вызов статически, на этапе компиляции. Предположим, что мы определили виртуальную функцию isA()
Полиморфизм
Полиморфизм Полиморфизм представляет собой способность вируса в процессе работы менять свой код таким образом, чтобы максимально затруднить процесс своего обнаружения путем сигнатурного сканирования и частично эвристики.Особо следует отметить тот факт, что и сама
Полиморфизм
Полиморфизм При наследовании, требование статической типизации, о котором говорилось выше, становится ограничивающим, если бы оно означало, что каждая сущность типа C может быть связана только с объектом точно такого же типа С. Например в системе управления навигацией
Полиморфизм
Полиморфизм Иерархии наследования позволяют достаточно гибко работать с объектами, сохраняя надежность статической типизации. Поддерживающие их методы: полиморфизм и динамическое связывание - одни из самых фундаментальных аспектов архитектуры ПО, обсуждаемой в этой
Статический тип, динамический тип
Статический тип, динамический тип Название последнего свойства предполагает различение "статического типа" и "динамического типа". Тип, который используется при объявлении некоторого элемента, является статическим типом соответствующей ссылки. Если во время выполнения
Статический механизм
Статический механизм Устранить последнее неясности в понимании закрепленного объявления поможет следующее замечание: это чисто статический механизм, не предполагающий никаких изменений объектов в период выполнения. Все ограничения могут быть проверены в период