Подскажите по гетерогенному контейнеру C++

Тема в разделе "LANGS.C", создана пользователем Rel, 2 сен 2011.

  1. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    допустим, есть базовый класс:
    Код (Text):
    1. class Base
    2. {
    3.     public:
    4.         static  Base* Create();
    5.         virtual void  Release();
    6.  
    7.         virtual uint32_t Type() { return TYPE_BASE; }
    8. };
    он олицетворяет пустой объект... у него есть N-наследников, которые, перегружая интерфейсные функции базового класса, олицетворяют каждый свой объект... все объекты сами себя создают с помощью статической функции Create и сами себя удаляют с помощью виртуальной функции Release, конструкторы, деструкторы, операторы new и delete закрыты в базовом классе:
    Код (Text):
    1. class ChildA : public Base
    2. {
    3.     public:
    4.         static  Base* Create();
    5.         virtual void  Release();
    6.  
    7.         virtual uint32_t Type() { return TYPE_CHILD_A; }
    8. };
    9.  
    10. class ChildB : public Base
    11. {
    12.     public:
    13.         static  Base* Create();
    14.         virtual void  Release();
    15.  
    16.         virtual uint32_t Type() { return TYPE_CHILD_A; }
    17. };
    то есть указатель на базовый класс представляет собой гетерогенный контейнер для создаваемых наследников ChildA - ChildX, тип объекта в таком гетерогенном контейнере возможно получить воспользовавшись виртуальной функцией Type... вопрос в том, возможно ли как то (так как идеалогически класс Base олицетворяет пустой класс), асоциировать с нулевым указателем (Base* pObj = NULL;) виртуальные функции базового класса? то есть допустим, если вызывается функция Type для нулевого указателя на Base, то использовалась бы функция из состава класса Base... понятно, что указатель на таблицу виртуальных функций лежит внутри класса, и при простом таком вызове будет ошибка обращения по нулевым указателям... как такую задачу лучше всего решить? думал о том, чтобы сделать обертку, унаследованную от Base и содержащую внутри себя указатель на потомка Base, и уже в перегруженных функциях делать проверку указателя внутри на NULL и возвращать соответствующие значения, но это решение мне как то не нравится, кажется, что есть более красивое решение... что подскажите?
     
  2. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Не понятно зачем это нужно. Но может подойдёт паттерн шаблонный метод? Невиртуальная функция вызывает виртуальную и соответственно проверяет на NULL this. Но для чего всё это?
     
  3. sergegers

    sergegers New Member

    Публикаций:
    0
    Регистрация:
    8 июн 2008
    Сообщения:
    172
    вероятнее всего кривой дизайн
     
  4. Nafanya

    Nafanya Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    581
    Rel
    Дружеский совет:
    1) static Base* Create();
    Данный метод присутствует и в базовом и в производных классах и при том он обычный, не виртуальный.
    Майерс, Effective C++, правило 36 - Никогда не переопределяйте наследуемые невиртуальные функции.

    Функции типа Create называют фабричными или виртуальными конструкторами - в зависимости от передаваемых параметров они способны создавать объекты различных типов. Оставьте её только в базовом классе.

    2)
    Код (Text):
    1. class ChildB : public Base
    2. {
    3.     public:
    4.         static  Base* Create();
    5.         virtual void  Release();
    6.  
    7.         virtual uint32_t Type() { return TYPE_CHILD_A; }
    8. };
    Принято писать ключевое слово virtual для виртуальных функций только в базовом классе. В производных virtual не пишут никогда, чтобы не вводить в заблуждение программистов.

    3) Нужен ли метод Release, возможно что будет достаточно применения виртуального деструктора. Хотя я точно не знаю с какой целью вы используетет Release.
     
  5. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Nafanya
    По всем этим пунктам нет той однозначности о которой Вы пишите. Ну или хотя-бы аргументируйте этот копипаст мыслей.

    Потому-что это написал Майерс? Не всегда нужно работать с классом через базовый тип, отсюда виртуальность при переопределении не всегда нужна.

    Базовый тип должен знать о производных?

    Заблуждение в чём? Что функция виртуальна только начиная с данного класса? Это маловероятно. Наоборот удобно видеть виртуальность не залезая в дебри базового типа.

    Всё это не более чем догмы, для начинающих.
     
  6. Nafanya

    Nafanya Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    581
    Booster
    Программисты обычно различают наследование интерфейса и наследование реализации, если подробней - то есть 3 варианта:
    1) Наследование интерфейса virtual void func() = 0;
    2) Наследование интерфейса и реализации по умолчанию virtual void func();
    3) Наследование интерфейса и обязательной реализации void func();

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

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

    В то же время Вы вдруг пытаетесь переопределить её обязательную реализацию в производном - что недопустимо, т.к. нарушается логика. Если операция выполняется по-разному для объекта базового и для объекта производного класса(необходимо обеспечить различное поведение), то необходимо наследовать интерфейс и реализацию по умолчанию, что достигается за счёт использования виртуальных функций.
     
  7. Nafanya

    Nafanya Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    581
    Скотт Мейерс пишет не для начинающих. На обложке книги Effective STL- Уровень: опытный/эксперт, Серия: библиотека программиста.

    Единственное - жаль, что в Effective C++, More Effective C++ о практическом применении шаблонов дана довольно скудная информация.
     
  8. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Nafanya
    Кто это сказал? Программист волен поступать по своему усмотрению. Вы снова пишите заученные постулаты. Семантика может быть различной. virtual не определяет логику использования или намерений, это лишь особенность вызова. Если объект приводится к базовому типу, то почему он не может вести себя как базовый?

    Написать можно что угодно, бумага всё стерпит. Постулаты там общеизвестные, но только эксперт умеет мыслить сам, а не заученными истинами.
     
  9. Nafanya

    Nafanya Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    581
  10. Nafanya

    Nafanya Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    581
    Booster
    Лично мне эти постулаты очень помогли, после прочтения данных книг я разложил для себя всё по полочкам, нет больше каши, и неразберихи - благодаря правилам Майерса создаются точные, строгие, как в математике рамки, за которые нельзя выходить, уже точно представляешь - как можно писать код, а как нельзя.
     
  11. sergegers

    sergegers New Member

    Публикаций:
    0
    Регистрация:
    8 июн 2008
    Сообщения:
    172
    Вообще, переопределение статических функций может использоваться в CRTP для статического полиморфизма
     
  12. Guest

    Guest Guest

    Публикаций:
    0
    Rel
    Если я правильно все понял то тебе может помочь создание класса BaseNull который будет унаследован от Base, он должен быть статичным, далее сделай контроль за указателями (какие-нибудь обертки аля hand made SmartPtr), тогда ты можешь вместо NULL возвращать указатель на этот класс и ошибок обращения по нулевому указателю не будет, предположу что можно перегрузить операторы сравнения, которые позволят сравнить умный указатель, который ссылается на BaseNull, с nullptr и вернуть TRUE.

    ИМХО: Вообще бы здесь использовать абстрактную фабрику, а если процессс создания сложный то добавить фасад + строитель. В этой фабрике уже регистрировать строителей классов. Тогда будет централизованный узел отвечающий за создание классов, не будет такого хардкода и фрагментированной статики разбросанной по проекту. Возможно в контексте задачи оно и не надо.
     
  13. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    спасибо всем за ответы... решил создать контейнер в стиле Александрескувских интеллектуальных указателей для объектов...

    ЗЫ кстати посоветуйте какую-нить книжку интересную почитать в стиле Современное проектироание на С++ или может что-то по C++0x...