Обращение к защищенному деструктору из наследника
От: RikkiTikkiTavi Россия  
Дата: 14.07.11 09:37
Оценка:
Здравствуйте!

Вроде бы неплохо знаю С++, но вот столкнулся с непоняткой:
class A
{
protected:
    A() {}
    virtual ~A() {}
};

class B
    : public A
{
protected:
    B() {}
    virtual ~B() {}
public:
    void deleter(A* p) {
        if (p)
            delete p;            error C2248: 'A::~A' : cannot access protected member declared in class 'A'
    }
};



Почему не могу вызвать деструктор базового класса из наследника?
Re: Обращение к защищенному деструктору из наследника
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 14.07.11 09:48
Оценка:
Здравствуйте, RikkiTikkiTavi, Вы писали:

RTT>Здравствуйте!


RTT>Вроде бы неплохо знаю С++, но вот столкнулся с непоняткой:

RTT>
RTT>class A
RTT>{
RTT>protected:
RTT>    A() {}
RTT>    virtual ~A() {}
RTT>};

RTT>class B
RTT>    : public A
RTT>{
RTT>protected:
RTT>    B() {}
RTT>    virtual ~B() {}
RTT>public:
RTT>    void deleter(A* p) {
RTT>        if (p)
RTT>            delete p;            error C2248: 'A::~A' : cannot access protected member declared in class 'A'
RTT>    }
RTT>};
RTT>


RTT>

RTT>Почему не могу вызвать деструктор базового класса из наследника?

Потому что класс A ничего не знает о классе B, а деструктор protected. Ты хочешь удалить не this (можно), а левый объект класса A.
Маньяк Робокряк колесит по городу
Re[2]: Обращение к защищенному деструктору из наследника
От: RikkiTikkiTavi Россия  
Дата: 14.07.11 10:21
Оценка:
Здравствуйте, Marty, Вы писали:
M>Ты хочешь удалить не this (можно), а левый объект класса A.
это что значит?

я хочу иметь делитер для класса А, но не хочу, чтобы он был публичным в А (так, ессно, скомпилится) — как мне быть?
Re[3]: Обращение к защищенному деструктору из наследника
От: avbochagov Россия  
Дата: 14.07.11 10:25
Оценка:
Здравствуйте, RikkiTikkiTavi, Вы писали:

RTT> M>Ты хочешь удалить не this (можно), а левый объект класса A.


RTT> это что значит?


RTT> я хочу иметь делитер для класса А, но не хочу, чтобы он был публичным в А (так, ессно, скомпилится) — как мне быть?


Используйте friend
avalon 1.0rc3 rev 416, zlib 1.2.3
Re[3]: Обращение к защищенному деструктору из наследника
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 14.07.11 10:29
Оценка:
Здравствуйте, RikkiTikkiTavi, Вы писали:

RTT>Здравствуйте, Marty, Вы писали:

M>>Ты хочешь удалить не this (можно), а левый объект класса A.
RTT>это что значит?
Ну то и значит. Класс A ничего не знает о классе B, и разрушение запрещено. Если ты разрушаешь объект класса B, то автоматом вызовется деструктор класса A, доступ к нему разрешен для наследников (он protected, а не private)

RTT>я хочу иметь делитер для класса А, но не хочу, чтобы он был публичным в А (так, ессно, скомпилится) — как мне быть?

class B;
class A
{
    friend class B;
};

При этом необязательно, чтобы B был наследником.

Если не хочешь всем предоставлять возможность удалять A, то определись кто это будет делать. Метод другого класса, или отдельная функция, и сделай его/её fiend'ом класса A.
Маньяк Робокряк колесит по городу
Re[3]: Обращение к защищенному деструктору из наследника
От: 24  
Дата: 14.07.11 10:29
Оценка: +1
Здравствуйте, RikkiTikkiTavi, Вы писали:

M>>Ты хочешь удалить не this (можно), а левый объект класса A.

RTT>это что значит?

Класс В содержит в себе подобъект класса А. Из В можно обратиться к протектед части только собственного подобъекта А, а к остальным объектам А — нельзя.
Re: Обращение к защищенному деструктору из наследника
От: Bell Россия  
Дата: 14.07.11 10:35
Оценка: 4 (1)
Здравствуйте, RikkiTikkiTavi, Вы писали:

RTT>

RTT>Почему не могу вызвать деструктор базового класса из наследника?

Потому что потомок имеет доступ к защищенным членам только своего базового подобъекта.
Разрулить можно, например, вот так:

class A
{
protected:
    A() {}
    virtual ~A() {}

    void deleter(A* p) {
        if (p)
            delete p;
    }
};

class B : public A
{
protected:
    B() {}
    virtual ~B() {}
public:
    void deleter(A* p) {
        A::deleter(p);//используем защищенный метод своего подобъекта
    }
};
Любите книгу — источник знаний (с) М.Горький
Re: Обращение к защищенному деструктору из наследника
От: Erop Россия  
Дата: 14.07.11 14:20
Оценка:
Здравствуйте, RikkiTikkiTavi, Вы писали:


RTT>Почему не могу вызвать деструктор базового класса из наследника?


Потому, что у тебя нет доступа к защищённым членам брата и предка.
Может ты опишешь что именно тебе надо как-то более понятно? Не в терминах "хочу написать такую-то функцию", а в терминах "имею такую-то структуру данных, хочу того-то, тут не выходит потому-то"?..
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Обращение к защищенному деструктору из наследника
От: Alexander G Украина  
Дата: 14.07.11 15:18
Оценка:
Здравствуйте, Bell, Вы писали:

B>Потому что потомок имеет доступ к защищенным членам только своего базового подобъекта.


Меня это удивило, странное ограничение.
В том же операторе присваивания можно брать приватные члены другого себя.

Обойти ограничение вроде удалось:

#include "stdio.h"

struct B 
{
protected:
  virtual void f() {};
};

struct D : B
{
  void g(B* b)
  {
    (b->*&D::f)();
  }
};

struct O : B
{
protected:
  void f() { printf("done"); }
};

int main() 
{
  D d;
  O o;
  d.g(&o);
}
Русский военный корабль идёт ко дну!
Re: Обращение к защищенному деструктору из наследника
От: i-gen  
Дата: 14.07.11 19:42
Оценка: 1 (1)
Дело в том, что Вы внутри метода B::deleter обращаетесь к глобальному оператору delete который пытается вызвать деструктор A::~A — а это ему не позволено (деструктор не public, а оператор delete не член классов A или B.
Решением могло бы быть определение оператора delete (как члена) внутри класса A (а вместе с ним определять и симметричный оператор new).
Еще одним (возможным) решением было бы добавить в класс A запись: friend operator delete (так не пользовал — не уверен, что верно)
Re[3]: Обращение к защищенному деструктору из наследника
От: Erop Россия  
Дата: 14.07.11 20:48
Оценка: 4 (1)
Здравствуйте, Alexander G, Вы писали:

AG>В том же операторе присваивания можно брать приватные члены другого себя.

В любом методе можно брать защищённые члены базы другого себя. Нельзя у брата взять.

Идея простая очень. Вот есть у тебя какой-то класс- предок. Base например.
И у него есть защищённое поле, например. Есть два наследника. Каждый из наследников обеспечивает свой инвариант. И нехорошо если брат брату грязными лапами инвариант порушит

Ты напиши, таки, чего же тебе надо, НА САМОМ ДЕЛЕ? Нужда сделать так, чтобы объекты класса А могли разрушать только методы его наследника В, для меня какая-то подозрительная и неожиданная...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Обращение к защищенному деструктору из наследника
От: Alexander G Украина  
Дата: 14.07.11 20:55
Оценка:
Здравствуйте, Erop, Вы писали:

E>Идея простая очень. Вот есть у тебя какой-то класс- предок. Base например.

E>И у него есть защищённое поле, например. Есть два наследника. Каждый из наследников обеспечивает свой инвариант. И нехорошо если брат брату грязными лапами инвариант порушит

Понятно.

E>Ты напиши, таки, чего же тебе надо, НА САМОМ ДЕЛЕ?


Мне — ничего, к этому вопросу у меня чисто теоретический интерес
Русский военный корабль идёт ко дну!
Re[2]: Обращение к защищенному деструктору из наследника
От: Bell Россия  
Дата: 15.07.11 02:04
Оценка:
Здравствуйте, i-gen, Вы писали:

IG>Дело в том, что Вы внутри метода B::deleter обращаетесь к глобальному оператору delete который пытается вызвать деструктор A::~A — а это ему не позволено (деструктор не public, а оператор delete не член классов A или B.

Совершенно неверно. Не нужно путать выражение delete (delete expression) и оператор delete. Оператор delete никаких деструкторов не вызывает, его дело — освободить память. Работу по вызову деструктора и последующему вызову operator delete выполняет выражение delete, и делается все это в контексте использования, т.е. в данном случае в контексте метода B::deleter.

IG>Решением могло бы быть определение оператора delete (как члена) внутри класса A (а вместе с ним определять и симметричный оператор new).

Это решение к настоящей проблеме никак не относится.

IG>Еще одним (возможным) решением было бы добавить в класс A запись: friend operator delete (так не пользовал — не уверен, что верно)

Правильно, что не уверен. Неверно это.
Любите книгу — источник знаний (с) М.Горький
Re[3]: Обращение к защищенному деструктору из наследника
От: programmater  
Дата: 18.07.11 09:14
Оценка:
Здравствуйте, Alexander G, Вы писали:
AG>Обойти ограничение вроде удалось:
Да уж, мощно Вы ограничения обходите! От строчки
    (b->*&D::f)();

моя упаль пацтол . Маладэц, аффтар пешы исчо!
Re[4]: Обращение к защищенному деструктору из наследника
От: programmater  
Дата: 18.07.11 10:08
Оценка:
Здравствуйте, Erop, Вы писали:

E>Ты напиши, таки, чего же тебе надо, НА САМОМ ДЕЛЕ? Нужда сделать так, чтобы объекты класса А могли разрушать только методы его наследника В, для меня какая-то подозрительная и неожиданная...

На самом деле это проблема так называемого "избранного расширенного управления" (это мое корявое название). Не знаю как там у автора, но у меня похожая проблема возникает довольно регулярно, и наиболее ярко ее можно продемонстрировать на возможности создания/удаления объектов. Сейчас в общих чертах попытаюсь сформулировать задачу.
Должен быть некий контейнер, в котором хранятся некие объекты. API контейнера позволяет клиенту получить некий объект (в идеале — указатель на объект в контейнере), имеющий крайне ограниченную функциональность. Т.е. у клиента не должно быть возможности:
Т.е клиент имеет крайне ограниченный функционал и не имеет возможности нарушить работу всей системы. И контейнер, получив от клиента указатель на объект может быть уверен, что этот объект хранится в этом контейнере, а не "взят с потолка", т.е. клиент не может сам создать объект и отдать указатель контейнеру, типа на, вот теперь удали его.
При этом контейнер, естественно, должен иметь доступ к расширенному функционалу. Естественно, у контейнера должна быть возможность создавать/уничтожать объекты, а также задавать конфигурацию буферов_и_чего-то_еще_необходимого_для_работы. Т.е. речь идет о написании системы классов, где члены этой системы имеют более расширенный функционал, чем не-члены этой системы. Средств явно выразить такое отношение между классами в языке C++ нет, поэтому приходится изголяться с protected и наследованием. Очевидно, что контейнер не может быть наследником хранимого в нем элемента, поэтому я эту проблему решаю так:
class ClientItem
{
  protected:
    ClientItem();
    ~ClientItem();
    ConfigureItem(...);
  public:
//---------------------
};

class Container
{
  protected:
    class ContainedItem: public ClientItem
    {
       public:
         inline void ConfigureItem(...) {ClientItem::ConfigureItem(...);}
    };

    std::some_container<ContainedItem> m_Items;
  public:
    ClientItem* GetItem(....)
    {
      ContainedItem* p = FindExisting(...);
      if(p) {return static_cast<ClientItem*>(p);}
      m_Items.push_back(ContainedItem());
      std::some_container<ContainedItem>::reverse_iterator it = m_Items.rbegin();
      p = &(*it);
      p->ConfigureItem(...);
      return static_cast<ClientItem*>(p);
    }
};

особенно интересно становится, когда возвращается не указатель на объект, а некий промежуточный объект (возвращается по значению), который в своем деструкторе (вызов которого означает, что клиент закончил работу с объектом) запускает некое действие (сортировка данных, запуск потока и т.д.). Тут вообще получается дилемма на уровне взаимоисключающих параграфов. С одной стороны, клиент не должен мочь создавать этот промежуточный объект (т.к. это дело контейнера) и не должен его копировать (дабы избежать множественных запусков), но с другой стороны, т.к. объект возвращается по значению — у него должен быть и конструктор и конструктор копирования. И меня уже достало выходить из подобного противоречия прикручиванием подсчета ссылок .
ЗЫ. Решать проблему "расширенный функционал только для избранных" с помощью friend не хочу по одной причине: friend-ность не наследуется! . Т.е. если у меня есть Container, который friend к ClientItem (и поэтому имеет доступ к расширенному функционалу), и я захочу сделать MyCoolContainer: public Container, то наследник уже не будет иметь доступа к расширенному функционалу ClientItem. Поэтому и горожу огород в protected — секции Container в виде класса-аксессора_к_защищенным_членам_ClientItem. Если подскажешь более прямое решение задачи разграничения функционала "вот этим можно, а вот тем нет", буду признателен.
Re[5]: Обращение к защищенному деструктору из наследника
От: Erop Россия  
Дата: 18.07.11 13:36
Оценка:
Здравствуйте, programmater, Вы писали:

P>ЗЫ. Решать проблему "расширенный функционал только для избранных" с помощью friend не хочу по одной причине: friend-ность не наследуется! . Т.е. если у меня есть Container, который friend к ClientItem (и поэтому имеет доступ к расширенному функционалу), и я захочу сделать MyCoolContainer: public Container, то наследник уже не будет иметь доступа к расширенному функционалу ClientItem. Поэтому и горожу огород в protected — секции Container в виде класса-аксессора_к_защищенным_членам_ClientItem. Если подскажешь более прямое решение задачи разграничения функционала "вот этим можно, а вот тем нет", буду признателен.


Вот, пример, такого ограничения: http://www.rsdn.ru/forum/cpp/3456421.1.aspx
Автор: Erop
Дата: 05.07.09

Это в нужную сторону или нет? В частности, про соответствие указателя на элемент контейнеру, мне, например, не понятно, что будет, если есть несколько контейнеров (экземпляров)


Вообще, мне так кажется, что сама по себе идея прятать сложности реализации за хитрыми трюками с С++ прокси-объектами -- корявая. Всё равно пользователь должен понимать, что он делает. Так уж лучше пусть прямо как-то пишет.

А так много трюков есть.

Может можешь как-то конкретнее объяснить суть проблем?
Вот, я такой придумал пример, что у нас есть контейнер, который хранит в себе массивы с "головами". При этом контейнер рулит всеми буферами. Что-то в таком духе?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Обращение к защищенному деструктору из наследника
От: programmater  
Дата: 18.07.11 14:12
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, programmater, Вы писали:


P>>ЗЫ. Решать проблему "расширенный функционал только для избранных" с помощью friend не хочу по одной причине: friend-ность не наследуется! . Т.е. если у меня есть Container, который friend к ClientItem (и поэтому имеет доступ к расширенному функционалу), и я захочу сделать MyCoolContainer: public Container, то наследник уже не будет иметь доступа к расширенному функционалу ClientItem. Поэтому и горожу огород в protected — секции Container в виде класса-аксессора_к_защищенным_членам_ClientItem. Если подскажешь более прямое решение задачи разграничения функционала "вот этим можно, а вот тем нет", буду признателен.


E>Вот, пример, такого ограничения: http://www.rsdn.ru/forum/cpp/3456421.1.aspx
Автор: Erop
Дата: 05.07.09

E>Это в нужную сторону или нет?
Не совсем. Я не хочу ограничивать контейнер каким-либо заранее заданным способом создания. Контейнер имеет право выбирать, будет ли он каждый объект создавать по new, хранить его в векторе или в листе, или даже на стеке (хотя с трудом представляю ситуацию, когда такое будет возможно, но при желании придумать можно). А вот у клиента такого права нет.

E>Вообще, мне так кажется, что сама по себе идея прятать сложности реализации за хитрыми трюками с С++ прокси-объектами -- корявая. Всё равно пользователь должен понимать, что он делает. Так уж лучше пусть прямо как-то пишет.


Прокси-объект нужен из-за автоматического контроля его времени жизни. Ты же не будешь спорить, что CComPtr штука хорошая, или скажешь, что перед каждым return-ом (если их несколько) нужно руками для каждого COM-интерфейса вызывать Release()? Можно конечно обязать клиента все делать руками, но ИМХО с автоматическим контролем времени жизни проще.

E>А так много трюков есть.


E>Может можешь как-то конкретнее объяснить суть проблем?

E>Вот, я такой придумал пример, что у нас есть контейнер, который хранит в себе массивы с "головами". При этом контейнер рулит всеми буферами. Что-то в таком духе?
Ну типа того (только наворочаннее). Контейнер хранит распарсеный а-ля XML файл, айтемы ссылаются друг на друга (и на кусок файла, весь считанный файл тоже хранится в контейнере). И вот надо запретить клиенту подсовывать контейнеру "левые" айтемы (а для этого достаточно запретить клиенту их создавать), а также менять конфигурацию "связанных айтемов". При этом для контейнера таких ограничений быть не должно.
Re[7]: Обращение к защищенному деструктору из наследника
От: Erop Россия  
Дата: 18.07.11 14:24
Оценка:
Здравствуйте, programmater, Вы писали:

E>>Вот, пример, такого ограничения: http://www.rsdn.ru/forum/cpp/3456421.1.aspx
Автор: Erop
Дата: 05.07.09

E>>Это в нужную сторону или нет?
P>Не совсем. Я не хочу ограничивать контейнер каким-либо заранее заданным способом создания. Контейнер имеет право выбирать, будет ли он каждый объект создавать по new, хранить его в векторе или в листе, или даже на стеке (хотя с трудом представляю ситуацию, когда такое будет возможно, но при желании придумать можно). А вот у клиента такого права нет.

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

P>Прокси-объект нужен из-за автоматического контроля его времени жизни. Ты же не будешь спорить, что CComPtr штука хорошая, или скажешь, что перед каждым return-ом (если их несколько) нужно руками для каждого COM-интерфейса вызывать Release()? Можно конечно обязать клиента все делать руками, но ИМХО с автоматическим контролем времени жизни проще.


Ну конкретнее хорошо бы показать, что ты имеешь в виду. А то у меня сразу возникает вопрос про групповые операции, например.

P>Ну типа того (только наворочаннее). Контейнер хранит распарсеный а-ля XML файл, айтемы ссылаются друг на друга (и на кусок файла, весь считанный файл тоже хранится в контейнере). И вот надо запретить клиенту подсовывать контейнеру "левые" айтемы (а для этого достаточно запретить клиенту их создавать), а также менять конфигурацию "связанных айтемов". При этом для контейнера таких ограничений быть не должно.



Ну, дык, казалось бы, требуешь в "запретной" части интерфейса предъявить какой-то токен, которого у пользователя нет. Или, как вариант, имеешь метод получения интерфейса на модификацию, для получения которого нужно предъявить токен.

Ещё можно доступ к объектам иметь только через хэндлы. Так что вообще мимо кнтейнера доступа не получишь никакого.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: Обращение к защищенному деструктору из наследника
От: programmater  
Дата: 18.07.11 15:26
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, programmater, Вы писали:


E>Ну тут также всё. Просто конкретно этот контейнер выбрал такой способ создания...

E>Речь-то шла не о том, где создаются объекты, а о том, как разграничитвать доступ...
Значит я не совсем понял Ваше решение. И сейчас не очень понимаю. Зачем-то приделали чисто виртуальную функцию checkSecretSeed, которая автоматически перекрывается в любом наследнике, при этом в функциях CreateNew объект явно размещается в динамической памяти. Как контейнер может изменить способ размещения объекта (не меняя кода самого объекта) я так и не понял...

E>Ну конкретнее хорошо бы показать, что ты имеешь в виду. А то у меня сразу возникает вопрос про групповые операции, например.

Это чего за зверь такой?

E>Ну, дык, казалось бы, требуешь в "запретной" части интерфейса предъявить какой-то токен, которого у пользователя нет. Или, как вариант, имеешь метод получения интерфейса на модификацию, для получения которого нужно предъявить токен.


E>Ещё можно доступ к объектам иметь только через хэндлы. Так что вообще мимо кнтейнера доступа не получишь никакого.

Вот предельный случай того, что я хочу — это и есть "хэндлы с контролем времени жизни". Ну вот давай на примере хэндлов и разберем, как без кастов к void* или int все реализовать. Очевидно, что в хэндле должен сидеть указатель на настоящий объект. Паблик конструктора у хэндла быть не должно (чтобы клиент не мог сам его создать). Но конструктор должен быть (чтобы контейнер мог его создать). Контейнер должен иметь возможность "добыть" из хэндла "настоящий указатель" (чтобы обрабатывать запросы от клиента), но у клиента такой возможности быть не должно. И как такую структуру реализовать? Я пока ничего умнее чем "классы-аксессоры" и подсчет ссылок не придумал:


class SomeContainer
{
  protected:
    // Собственно объект, хранящийся в контейнере
    // никаких хитростей типа защищенного конструктора и т.д.
    class SomeItemImpl
    {
    };
  public:
    // А вот дальше начинаются хитрости

    // класс хэндла, который будет получать клиент
    class SomeHandle
    {
      protected:
        SomeItemImpl* m_pImpl;
        SomeHandle(SomeItemImpl* pImpl) : m_pImpl(pImpl) {};
      // дальше начинается ваще мрак. Статическая функция создания объекта (возврат по значению).
      // вся хитрость в том, что она защищенная, а значит вызвать ее может только наследник.
        static SomeHandle CreateSomeHandle(SomeItemImpl* pImpl) {return SomeHandle(pImpl);}
        static SomeItemImpl* GetImpl(const SomeHandle H) {return H.m_Impl;}
    };

  protected:
  // Здесь объявляется класс-аксессор, который позволяет контейнеру работать с хэндлами
  // Делает для контейнера protected-функции public-ами
    class SomeHandleAcc: public SomeHandle
    {
      public:
        static SomeHandle CreateSomeHandle(SomeItemImpl* pImpl) {return SomeHandle::CreateSomeHandle(pImpl);}
        static SomeItemImpl* GetImpl(const SomeHandle H) {return SomeHandle::GetImpl(H);}
    };
  public:
    // ну и дальше идет работа с хэндлами
    SomeHandle GetItem(...)
    {
      SomeItemImpl* pItem = FindItem(...); // Добываем из контейнера указатель на нужный клиенту объект
      return SomeHandleAcc::CreateSomeHandle(pItem);
    }

    DoSomeWithObject(SomeHandle H)
    {
      DoSomeWithObjectImpl(SomeHandleAcc::GetImpl(H));
    }
};



Вот как-то так. Заниматься такой писаниной я уже задоблался. Если подскажешь как все то же самое сделать просче, буду благодарен.
А если сюда захотеть прикрутить контроль времени жизни, да еще и без подсчета ссылок — то вообще ужыс получается
Re[9]: Обращение к защищенному деструктору из наследника
От: Erop Россия  
Дата: 18.07.11 17:08
Оценка:
Здравствуйте, programmater, Вы писали:

P>Здравствуйте, Erop, Вы писали:


E>>Здравствуйте, programmater, Вы писали:


E>>Ну тут также всё. Просто конкретно этот контейнер выбрал такой способ создания...

E>>Речь-то шла не о том, где создаются объекты, а о том, как разграничитвать доступ...
P>Значит я не совсем понял Ваше решение. И сейчас не очень понимаю. Зачем-то приделали чисто виртуальную функцию checkSecretSeed, которая автоматически перекрывается в любом наследнике, при этом в функциях CreateNew объект явно размещается в динамической памяти. Как контейнер может изменить способ размещения объекта (не меняя кода самого объекта) я так и не понял...

Ну там есть некая фабрика, которая умеет производить наследников некоего своего интерфейса. При этом наследников пишет вообще клиент, и они никак на фабрику не завязаны. Единственное требование -- наследоваться от того самого интерфейса.
При этом наследники получаются абстрактными классами, поэтому создавать их экземпляры нельзя. А фабрика может их создавать, так как создаёт не их, а их наследников.

При этом тут существенно не то, что это фабрика, а то, как разграничивается доступ к интерфейсам между клиентским кодом и кодом фабрики. Точно так же контейнер может хранить внутри себя каких-то хитрых наследников или агрегаторов внешнего класса. А наружу отдавать указатели/ссылки на этот внешний класс.

E>>Ну конкретнее хорошо бы показать, что ты имеешь в виду. А то у меня сразу возникает вопрос про групповые операции, например.

P>Это чего за зверь такой?

Ну типа скобочку открыли-скобочку закрыли — индекс перестроили — скобочку открыли-скобочку закрыли — индекс перестроили — скобочку открыли-скобочку закрыли — индекс перестроили... А я не хочу в промежутках индекс перестраивать, например.

P>Вот предельный случай того, что я хочу — это и есть "хэндлы с контролем времени жизни". Ну вот давай на примере хэндлов и разберем, как без кастов к void* или int все реализовать. Очевидно, что в хэндле должен сидеть указатель на настоящий объект.

Почему? Хэндлом может быть индекс в массиве, например...

P>Паблик конструктора у хэндла быть не должно (чтобы клиент не мог сам его создать). Но конструктор должен быть (чтобы контейнер мог его создать). Контейнер должен иметь возможность "добыть" из хэндла "настоящий указатель" (чтобы обрабатывать запросы от клиента), но у клиента такой возможности быть не должно. И как такую структуру реализовать? Я пока ничего умнее чем "классы-аксессоры" и подсчет ссылок не придумал:


Опять же, странная логика. Почему хэндл не может быть просто значением?

P>Вот как-то так. Заниматься такой писаниной я уже задоблался. Если подскажешь как все то же самое сделать просче, буду благодарен.


Я вот чего никак не пойму. Почему нельзя сделать отдельный контейнер, который рулит отображением хэндлов на объекты, и в нужных местах его агрегировать просто и всё? Зачем копи-паста?

template<typename T>
class HandleMachine {
    enum { EndOfList = -1 };
    union item_t {
        T* pObj;
        int next;

        item_t( int n = EndOfList ) { next = n; }
        item_t( T* p ) { pObj = p; }
    };
    typedef std::vector<item_t> handles_t;
public:
    HandleMachine() : freeHandles( EndOfList ) {}
    class CPtr {
        HandleMachine* machine;
        int handle;

        CPtr( HandleMachine* m, int h ) : machine( m ), handle( h ) {}
    public:
        CPtr() : machine( 0 ), handle( EndOfList ) {}

        T* operator->() { return machine->handles[handle].pObj; }
        const T* operator->() const { return machine->handles[handle].pObj; }
    };

    CPtr Add( T* pObj );
    CPtr FindObject( T* );
    void InvalidateHandle( CPtr );
private:
    handles_t handels;
    int freeHandles;

    int allocHandle( T* pObj )
    {
        int res = freeHandles;
        if(freeHandles != EndOfList) {
            item_t& item = handles[freeHandles];
            freeHandles = item.next;
            item.pObj = pObj;
        } else {
            res = handles.size();
            handles.push_back( item_t( pObj ) );
        }
        
        return res;
    }
    T* freeHandle( int handle )
    {
        assert( 0 <= handle && handle < handles.size() );
        item_t& item = handles[handle];
        T* res = item.pObj;
        item.next = freeHandles;
        freeHandles = handle;
        return res;
    }
};
типа так как-то делаешь, и клиентский код фиг два добудет твои данные.

P>А если сюда захотеть прикрутить контроль времени жизни, да еще и без подсчета ссылок — то вообще ужыс получается


Я не совсем понимаю, что и зачем тебе надо. Если речь идёт о перестройке индексов, то может их просто по требованию строить, а при модификациях сбрасывать?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.