Comments 48
Забавно было бы посмотреть, а кому реально это пригодится и в каком случае.
Обычно, как вы правильно заметили, процесс создания множества разных объектов одним методом делается либо с помощью фабричного метода (BaseClass* obj = BaseClass::Create(...)), либо с помощью абстрактной фабрики (BaseClass* obj = Fabric::Get(...)->Create(...)). А такое чудо-юдо я даже не знаю, где и применить.
Кстати, техника «конверта» и «письма» — это практически в чистом виде классический плюсовый smart pointer.
Обычно, как вы правильно заметили, процесс создания множества разных объектов одним методом делается либо с помощью фабричного метода (BaseClass* obj = BaseClass::Create(...)), либо с помощью абстрактной фабрики (BaseClass* obj = Fabric::Get(...)->Create(...)). А такое чудо-юдо я даже не знаю, где и применить.
Кстати, техника «конверта» и «письма» — это практически в чистом виде классический плюсовый smart pointer.
Кстати, техника «конверта» и «письма» — это практически в чистом виде классический плюсовый smart pointer.Не совсем. Здесь конверт и письмо — это один и тот же класс. А умный указатель — это отдельный класс, который вообще ничего не знает о типе объекта, который в нём хранится.
Моё дело познакомить Вас с такой возможностью, а использовать её или нет — решать Вам.
Кстати, «письмо и конверт» — это, можно сказать, особый вид паттерна State.
Кстати, «письмо и конверт» — это, можно сказать, особый вид паттерна State.
Или даже Proxy?
UFO just landed and posted this here
Взаимно.
UFO just landed and posted this here
«…чтобы обеспечить заявленную логику, базовый класс ДОЛЖЕН знать о всех потомках, которые будут от него унаследованы когда-нибудь».
Это плата за то чтобы клиент базового класса о потомках даже не подозревал.
Это плата за то чтобы клиент базового класса о потомках даже не подозревал.
Это плата за то что все конструкторы наследников — приватные. А зачем?
Поясню свою мысль:
Зачем такое ограничение? В данном примере объекты-то получатся взаимозаменяемыми, вне зависимости от того, созданы ли они через Skill(int) или непосредственно.
Если это ограничение убрать, и конструкторы сделать открытыми, friend не нужен.
В производных классах техник FireSkill, WoodSkill и т.д. конструкторы по умолчанию закрыты, но базовый класс Skill объявлен как friend, что позволяет создавать объекты этих классов только внутри класса Skill.
Зачем такое ограничение? В данном примере объекты-то получатся взаимозаменяемыми, вне зависимости от того, созданы ли они через Skill(int) или непосредственно.
Если это ограничение убрать, и конструкторы сделать открытыми, friend не нужен.
Фабрика в данном случае не причем. Вирутальный конструктор реализуется посредством виртуальной ф-ии clone(), которая возвращает указатель на реально содержащийся в классе тип, а никак не посредством фабрики.
Фабрика вообще-то самый популярный способ реализации «виртуального конструктора». А метод clone (оно же паттерн прототип) это по сути некий аналог аналог «виртуального» конструктора копии, более узкий случай.
Вообщето весь прогркссивный мир пдля такиз цеоей пользуется фабриками. То, что вы тут накуралесили — ЕРЕСЬ.
Спасибо за то что поделились необычной техникой.
Я ещё не придумал как это всё можно использовать, но зато в голову пришла безумная мысль. Скажем, в конверте может храниться не один указатель, а несколько. Например, в случае водно-земельного заклинания конверт мог бы хранить указатели на водное и земляное заклинания. В идеале, вызов skills[i]->Attack(); должен вызывать виртуальные функции-члены для всех внутренних указателей. К сожалению, так красиво сделать не удастся, зато вот такой вызов реализовать вполне можно: skills[i]->Make(&Skill::Attack);
Конечно, приведённый пример скорее из серии эквилибристики, чем реально полезных вещей. Хотя кто знает… )
Я ещё не придумал как это всё можно использовать, но зато в голову пришла безумная мысль. Скажем, в конверте может храниться не один указатель, а несколько. Например, в случае водно-земельного заклинания конверт мог бы хранить указатели на водное и земляное заклинания. В идеале, вызов skills[i]->Attack(); должен вызывать виртуальные функции-члены для всех внутренних указателей. К сожалению, так красиво сделать не удастся, зато вот такой вызов реализовать вполне можно: skills[i]->Make(&Skill::Attack);
Конечно, приведённый пример скорее из серии эквилибристики, чем реально полезных вещей. Хотя кто знает… )
Немного нужно порефакторить, но в целом вот: dumpz.org/10589/
Да, примерно об этом я и говорил. Вот только описывать базовый вызов для каждой функции очень муторно и потенциальный источник ошибки. Есть вариант сделать это банально через пропроцессор, но я бы предпочёл чуть более сложный в вызове универсальный вариант:
void Skill::Make (void (Skill::*foo)()) { for (size_t i = 0; i < mLetters.size(); ++i) (mLetters[i]->*foo)(); } ...skills[i]->Make(&Skill::Foo);
а в чем плюсы то такого полета фантазии?
по сравнению с фабриками мы лишаемся наглядности.
параметр, который на самом деле представляет тип — это ахтунг и фундамент для долгих дебагов, ибо лишает нас помощи компилятора и переносит контроль типов на наши плечи. одно изменение — и компилятор не сообщит обо всех ошибках. тока Control+F. А если кто дефайнул констант по-своему…
Базовый класс, который знает наследников — это пять… Игра явно не стоит свеч.
по сравнению с фабриками мы лишаемся наглядности.
параметр, который на самом деле представляет тип — это ахтунг и фундамент для долгих дебагов, ибо лишает нас помощи компилятора и переносит контроль типов на наши плечи. одно изменение — и компилятор не сообщит обо всех ошибках. тока Control+F. А если кто дефайнул констант по-своему…
Базовый класс, который знает наследников — это пять… Игра явно не стоит свеч.
я бы отнёс подобный код к запретным техникам (kinjutsu, 禁術)… не изящный он какой-то
спасибо, способ оригинальный и статья полезная, лучше кучи других в которых описываются элементарные вещи, которые легко находятся в любом учебнике.
Достаточно странный метод, если честно.
Вся история с виртуальными функциями придумана чтобы избежать switch...case по типам. А тут он в явном виде присутствует :(
Вся история с виртуальными функциями придумана чтобы избежать switch...case по типам. А тут он в явном виде присутствует :(
Вообще само по себе определение «виртуальный конструктор» не совсем корректно — виртуальный метод выбирается зависимо от реальной интстанции объекта стоящего за указателем на базовый класс. К конструктору то применить вообще-то нельзя :)
С другой стороны, если пойти в торону метаклассов, то конструктор есть методом метакласса, и тогда надо делать объект Type с виртуальным методом CreateInstance. Получим абстрактную фабрику.
В этом примере действительно смущает оператор switch, такой код в принципе несложно переписать с фабриками и он будет выглядеть более изящно. Для выбора по элементу энама можно сделать например массив фабрик.
Впрочем это все философия на тему что такое «виртуальный конструктор» вообще. Термин не определен языком С++, так что спорсить особо некуда :)
С другой стороны, если пойти в торону метаклассов, то конструктор есть методом метакласса, и тогда надо делать объект Type с виртуальным методом CreateInstance. Получим абстрактную фабрику.
В этом примере действительно смущает оператор switch, такой код в принципе несложно переписать с фабриками и он будет выглядеть более изящно. Для выбора по элементу энама можно сделать например массив фабрик.
Впрочем это все философия на тему что такое «виртуальный конструктор» вообще. Термин не определен языком С++, так что спорсить особо некуда :)
А кто-нибудь встречал Коплиена в электронном варианте? Я не нашёл.
Jim Coplien значится в рецензентах и «благодарностях» к многим книгам других известных авторов, даже у Страуструпа по-моему… Так что, возможно, Вы его уже читали :)
Я не буду кричать, что это ересь или бред. Просто потому, что это ни разу не так.
Необычно — да. Я о такой возможности не задумывался. Может быть, где-то пригодится.
В любом случает, спасибо автору статьи за труд :)
Необычно — да. Я о такой возможности не задумывался. Может быть, где-то пригодится.
В любом случает, спасибо автору статьи за труд :)
не совсем понял вот это:
>Виртуальный конструктор должен быть определен ниже всех производных классов,
>чтобы не пришлось париться с опережающими объявлениями (forward declarations), так
>как внутри него создаются объекты производных классов.
это вы вообще, или про конкретную вашу реализацию?
>Виртуальный конструктор должен быть определен ниже всех производных классов,
>чтобы не пришлось париться с опережающими объявлениями (forward declarations), так
>как внутри него создаются объекты производных классов.
это вы вообще, или про конкретную вашу реализацию?
А если мне нужно будет через пол года добавить параметризованный конструктор в наследника?
Если есть шанс того, что Вы будете вносить подобные изменения, их нужно предусмотреть на этапе проектирования, а значит — подобный «мегахак» не для Вас.
Всего не предусмотришь. Я не сторонник мегапроектирования всей системы сразу и навеки. Лучше заранее избегать таких вот «мегахаков» и использовать гибкие решения, чтобы потом не пришлось переписывать всю систему.
У этого паттерна гораздо больше недостатков, чем у аналогичных. В нем есть практики того, как делать не надо.
И все эти люди, которые пишут о том что он плохой, делают это не просто так.
Поэтому чтобы не совращать неокрепшие умы, стоит добавлять к этому паттерну приставку «анти».
И все эти люди, которые пишут о том что он плохой, делают это не просто так.
Поэтому чтобы не совращать неокрепшие умы, стоит добавлять к этому паттерну приставку «анти».
Все думал, думал чтоже мне напоминает данный паттерн. И вот вспомнил Curiously recurring template pattern (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). И как я удвился когда увидел, что автор тоже Джим Коплиен. Хочу сказать, что CRTP действительно интересный паттерн и когда-то сам его использовал. Слышал что в Adobe его пользовали и даже вроде в QT. А в статье видимо его динамический аналог.
И всё-же это ересь и бред. Да, так написать можно, но за такой код надо убивать, а потом увольнять. Он противоречит основным парадигмам ООП: получается, что базовый класс обязан знать всё о своих наследниках — сколько их, как они называются, сколько в них функций, с какими параметрами и т.д. И каждый раз при добавлении нового класса\функции нужно будет переписывать вообще всё, включая базовый класс и его конструктор.
Этот пример красиво и смачно плюёт на всё ООП в целом (и особо ярко на наследование с полиморфизмом в частности), забивает микроскопом гвозди и единственная от него польза — объяснить почему нужно пользоваться паттерном «фабрик» и не изобретать таких вот велосипедов-монстров.
Этот пример красиво и смачно плюёт на всё ООП в целом (и особо ярко на наследование с полиморфизмом в частности), забивает микроскопом гвозди и единственная от него польза — объяснить почему нужно пользоваться паттерном «фабрик» и не изобретать таких вот велосипедов-монстров.
Ересь и бред — это сначала убивать, а потом увольнять ;)
А вообще, мои друзья из компании, название которой нельзя называть, предложили реализовать то же самое через шаблоны при условии, что нет необходимости задавать тип во время выполнения. Там все ваши парадигмы соблюдены.
А вообще, мои друзья из компании, название которой нельзя называть, предложили реализовать то же самое через шаблоны при условии, что нет необходимости задавать тип во время выполнения. Там все ваши парадигмы соблюдены.
Sign up to leave a comment.
Виртуальный конструктор