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