вассал моего вассала - не мой вассал (friend и наследование)
От: programmater  
Дата: 06.05.06 17:54
Оценка:
Уважаемый All.
Есть задача — локализовать создание объектов. Т.е. есть объект-контейнер и есть итемы этого контейнера. Есть иерархия контейнеров, есть иерархия итемов. Задача в том, что итем не может существовать в пустоте — он обязан принадлежать контейнеру. Поэтому хочется сделать конструктор итема закрытым и разрешать создавать итемы только потомкам базового контейнера. Первое, что приходит в голову — объявить конструктор итема протектным и объявить BaseContainer friend-ом итема. Типа такого:

class Item
{
  friend class BaseContainer;
  protected:
   Item() {/*создание*/};
   virtual ~Item() {};
  public:
  // что-то
};
class DerivedItem1: public Item
{
  friend class BaseContainer;
  protected:
   DerivedItemItem1() {/*создание*/};
   virtual ~DerivedItem1() {};
  public:
  // что-то
};
class BaseContainer
{
  public:
    Item* CreateItem() 
    {
      Item* RetItem = new Item();
      // Воткнуть RetItem в контейнер
      return RetItem;
    }
    void DestroyItem(Item* it) {/* вытащить it из контейнера*/ delete it;}
};
// До этого момента все хорошо. Но вот появился DerivedContainer, поддерживающий DerivedItem1
// BaseContainer править не хочется. Пытаюсь писать
class DerivedContainer : public BaseContainer
{
  public:
    Item* CreateItemInDerivedContainer()
    {
      Item* RetItem = new Item();
      // Воткнуть в контейнер, но по-другому, чем в BaseContainer
      return RetItem;
    }
    DerivedItem1* CreateDerivedItem()
    {
      DerivedItem1* RetItem = new DerivedItem1();
      // Воткнуть в контейнер
      return RetItem;
    }
};


Все бы хорошо, но friend-ность не наследуется. Т.е. в наследнике BaseContainer-а я не могу создавать итемы!!! И при попытке вызвать new Item() в DerivedContainer я тут же получаю по глове от компилятора типа "не могу вызвать протектед функцию (си декларейшин оф Item::Item)". То, что DerivedContainer является наследником BaseContainer никого не волнует. Вот облом. Как обойти это ограничение или какие есть другие решения данной задачи (надеюсь задача ясна).
Спасибо.
Re: вассал моего вассала - не мой вассал (friend и наследова
От: Roman Odaisky Украина  
Дата: 06.05.06 18:06
Оценка:
Здравствуйте, programmater, Вы писали:

P>Есть задача — локализовать создание объектов. Т.е. есть объект-контейнер и есть итемы этого контейнера. Есть иерархия контейнеров, есть иерархия итемов. Задача в том, что итем не может существовать в пустоте — он обязан принадлежать контейнеру. Поэтому хочется сделать конструктор итема закрытым и разрешать создавать итемы только потомкам базового контейнера. Первое, что приходит в голову — объявить конструктор итема протектным и объявить BaseContainer friend-ом итема. Типа такого:


P>
...


P>Все бы хорошо, но friend-ность не наследуется. Т.е. в наследнике BaseContainer-а я не могу создавать итемы!!! И при попытке вызвать new Item() в DerivedContainer я тут же получаю по глове от компилятора типа "не могу вызвать протектед функцию (си декларейшин оф Item::Item)". То, что DerivedContainer является наследником BaseContainer никого не волнует. Вот облом. Как обойти это ограничение или какие есть другие решения данной задачи (надеюсь задача ясна).

P>Спасибо.

А если так?
template <typename T>
class CanConstruct
{
protected:
    static T constructItem() const
    {
        return T();
    }
};

class Item1
{
    friend class CanConstruct<Item1>;
    ...
};

class Container1: protected CanConstruct<Item1>
{
    ...
    T t = constructItem();
    ...
};

Не очень красиво, конечно, но должно (?) работать. Вызовы конструктора копирования любой уважающий себя компилятор выкинет.
До последнего не верил в пирамиду Лебедева.
Re: вассал моего вассала - не мой вассал (friend и наследова
От: rg45 СССР  
Дата: 06.05.06 19:11
Оценка: 11 (3)
"programmater" <34509@users.rsdn.ru> wrote in message news:1886899@news.rsdn.ru...
> Уважаемый All.
> Есть задача — локализовать создание объектов. Т.е. есть объект-контейнер и есть итемы этого контейнера. Есть иерархия контейнеров, есть иерархия итемов. Задача в том, что итем не может существовать в пустоте — он обязан принадлежать контейнеру. Поэтому хочется сделать конструктор итема закрытым и разрешать создавать итемы только потомкам базового контейнера. Первое, что приходит в голову — объявить конструктор итема протектным и объявить BaseContainer friend-ом итема. Типа такого:
>
>
> class Item
> {
>  friend class BaseContainer;
>  protected:
>   Item() {/*создание*/};
>   virtual ~Item() {};
>  public:
>  // что-то
> };
> class DerivedItem1: public Item
> {
>  friend class BaseContainer;
>  protected:
>   DerivedItemItem1() {/*создание*/};
>   virtual ~DerivedItem1() {};
>  public:
>  // что-то
> };
> class BaseContainer
> {
>  public:
>    Item* CreateItem() 
>    {
>      Item* RetItem = new Item();
>      // Воткнуть RetItem в контейнер
>      return RetItem;
>    }
>    void DestroyItem(Item* it) {/* вытащить it из контейнера*/ delete it;}
> };
> // До этого момента все хорошо. Но вот появился DerivedContainer, поддерживающий DerivedItem1
> // BaseContainer править не хочется. Пытаюсь писать
> class DerivedContainer : public BaseContainer
> {
>  public:
>    Item* CreateItemInDerivedContainer()
>    {
>      Item* RetItem = new Item();
>      // Воткнуть в контейнер, но по-другому, чем в BaseContainer
>      return RetItem;
>    }
>    DerivedItem1* CreateDerivedItem()
>    {
>      DerivedItem1* RetItem = new DerivedItem1();
>      // Воткнуть в контейнер
>      return RetItem;
>    }
> };
>

>
> Все бы хорошо, но friend-ность не наследуется. Т.е. в наследнике BaseContainer-а я не могу создавать итемы!!! И при попытке вызвать new Item() в DerivedContainer я тут же получаю по глове от компилятора типа "не могу вызвать протектед функцию (си декларейшин оф Item::Item)". То, что DerivedContainer является наследником BaseContainer никого не волнует. Вот облом. Как обойти это ограничение или какие есть другие решения данной задачи (надеюсь задача ясна).
> Спасибо.

В базовом классе контейнера можно определить шаблонную функцию для создания разных айтемов и вызывать эту функцию в производных контейнерах вместо оператора new:
class BaseContainer
{
protected:
  template<typename Item>   
  static Item* CreateItem()   {return new Item();}
};

class DerivedContainer : public BaseContainer
{
 public:
   Item* CreateItemInDerivedContainer()
   {
     Item* RetItem = CreateItem<Item>();
     // Воткнуть в контейнер, но по-другому, чем в BaseContainer
     return RetItem;
   }
   DerivedItem1* CreateDerivedItem()
   {
     DerivedItem1* RetItem = CreateItem<DerivedItem1>();
     // Воткнуть в контейнер
     return RetItem;
   }
};


Но вообще, сильно настораживает приведенное проектное решение: контейнер одновременно является также и фабрикой. Вообще контейнер по своей семантике должен обесперчивать хранение айтемов и реализовывать характерные для контейнера операции (вставка у даление и т.д.). Причем со всеми айтемами он должен работать идентично, через базовый интерфейс айтема. А создание объектов это другая задача и втискивать ее решение внутрь класса контейнера нерационально. Кроме того такие сильные зависимости между классами как дружба нужно стараться минимизировать. Если код сплошь и рядом усыпан объявлениями дружественности — это признак неудачного проектирования.

Если же вставка айтема в контейнер, все-таки, является специфичной для каждой пары Item-Container — то это известная задача о двойной диспетчеризации. Одним из возможных решений этой задачи является паттерн Visitor, но это уже отдельная, достаточно обширная тема.
Posted via RSDN NNTP Server 2.0
Я жду, когда этот пидарас
Автор: Bj777x
Дата: 22.06.20
либо удалит свое хамство здесь
Автор: Bj777x
Дата: 14.10 21:26
и здесь
Автор: Bj777x
Дата: 27.09 11:34
, либо восстановит ответы.
Re[2]: вассал моего вассала - не мой вассал (friend и наслед
От: programmater  
Дата: 07.05.06 12:42
Оценка:
Здравствуйте, rg45, Вы писали:

R>В базовом классе контейнера можно определить шаблонную функцию для создания разных айтемов и вызывать эту функцию в производных контейнерах вместо оператора new:

[кусь]
Грамотно. Правда возможно (и даже скорее всего) DerivedContainer-у потребуется доступ к не-паблик членам айтема. Но идея хорошая.
R>Но вообще, сильно настораживает приведенное проектное решение: контейнер одновременно является также и фабрикой.
Именно содержимое айтема очень сильно завязано на контейнер, так что контейнер является хозяином айтема.
R>Вообще контейнер по своей семантике должен обесперчивать хранение айтемов и реализовывать характерные для контейнера операции (вставка у даление и т.д.).
Это не совсем обычный контейнер. Я хочу, чтобы создать айтем "просто так" (без привязки к контейнеру) было невозможно. Тем более нельзя айтем вытащить из одного контейнера и всунуть в другой. Постараюсь объяснить. К примеру, у контейнера есть набор битмапов, из которых можно строить мозаику. Вот айтем и есть один из вариантов построения этой мозаики. Айтем содержит в себе ссылки (указатели) на подмножество битмапов своего контейнера. И что прикажете делать в операции "достать айтем из контейнера"? А если теперь такой айтем захотеть вставить в другой контейнер? все ссылки уже невалидные (в общем случае, например, первый контейнер уже удалили). Инициализировать значениями по умолчанию? Но тогда проще (и полезнее) будет создать новый айтем. Вся польза от айтема — это хранение вот этих ссылок. Так что айтем только родившись, уже должен принадлежать контейнеру. Его нельзя вытащить из контейнера (удалить можно, а вытащить ни-ни). Контейнер является и родителем, и убийцей айтема (удалить айтем через delete тоже нельзя, нужно вызывать у контейнера метод DeleteItem, чтобы контейнер правильно обработал это удаление и у него не осталось ссылок на этот айтем)

R>Причем со всеми айтемами он должен работать идентично, через базовый интерфейс айтема. А создание объектов это другая задача и втискивать ее решение внутрь класса контейнера нерационально.

Извне — да. У контейнера есть метод GetItem() (GetNextItem(), GetItem(int ItemIndex)), который возвращает указатель на базовый интерфейс айтема.
R>Кроме того такие сильные зависимости между классами как дружба нужно стараться минимизировать. Если код сплошь и рядом усыпан объявлениями дружественности — это признак неудачного проектирования.
Полностью согласен, не люблю я это. Но как решить данную конкретную задачу, пока не знаю.

R>Если же вставка айтема в контейнер, все-таки, является специфичной для каждой пары Item-Container — то это известная задача о двойной диспетчеризации. Одним из возможных решений этой задачи является паттерн Visitor, но это уже отдельная, достаточно обширная тема.

Двойная диспетчеризация тут будет, но для другого. Ведь все эти механизмы можно применять, когда айтем[ы] уже созданы. А для создания айтема какая тут диспетчеризация? Чью виртуальную функцию ты предлагаешь вызывать?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.