A>Но может быть лучше реализовать все с использованием стандартных контейнеров?
А можно примерчикорм подразнить, если не в напряг. А то у меня некоторые трудности с терминологией...
Здравствуйте, Al_, Вы писали:
Al_> А можно примерчикорм подразнить, если не в напряг. А то у меня некоторые трудности с терминологией...
да запросто
#include <vector>
int main(int argc, char *argv[])
{
typedef std::vector<int> st_vector; //vector - стандартный контейнер из библиотеки шаблонов (STL - standard
//template library). int - твой тип (TT) который ты "хранишь" в CQueueItem
st_vector v;
v.push_back(0); //добавляем элемент
v.pop_back(); //удаляем элемент
//а в сотальном есть еще много всяких контейнеров и алгоритмов для работы с ними (см. на сайте в разделе статьи)return 0;
}
Species come and go, but the earth stands forever fast...
Здравствуйте, Libra, Вы писали:
Al_>> А можно примерчикорм подразнить, если не в напряг. А то у меня некоторые трудности с терминологией... L>да запросто
L>
L>#include <vector>
L>int main(int argc, char *argv[])
{
typedef std::vector<int> st_vector; //vector - стандартный контейнер из библиотеки шаблонов (STL - standard
L> //template library). int - твой тип (TT) который ты "хранишь" в CQueueItem
L> st_vector v;
L> v.push_back(0); //добавляем элемент
L> v.pop_back(); //удаляем элемент
L> //а в сотальном есть еще много всяких контейнеров и алгоритмов для работы с ними (см. на сайте в разделе статьи)
L> return 0;
L>}
L>
Понятно. Но я хотел бы оставить свои классы. Их функциональность более ориентирована под решаемую задачу. (%
Чесно говоря там не очень понятна реализация желаемого.
Здравствуйте, Al_, Вы писали:
Al_>Хочется (:, чтобы экземпляры класса могли хранить указатели разных типов, а, методы класса, хотябы, возвращать эти указатели.
Уточни пожалуйста, что ты хочешь.
* чтобы для одного и того же класса (а не шаблона классов!) разные экземпляры хранили указатели разных типов
* чтобы для разных классов (и, соответственно, разных экземпляров) был один и тот же шаблон
Последний случай — тривиальный. Просто делаешь все что нужно шаблоном. Можно еще сделать функции-фабрики для автоматического вывода типа, наподобие такого:
А вот с первым — придется повозиться. Тут многое зависит от того, что именно и на какой стадии (компиляция, исполнение) ты хочешь делать с данными разных типов.
Перечислю некоторые возможности.
1. Данные — это объекты с одним и тем же интерфейсом.
Реализация: делаем методы интерфейса виртуальными, наследуем объекты от него, обращаемся к данным через этот интерфейс.
Все просто и естественно.
2. Требуется получать разные интерфейсы или еще как-то динамически приводить тип.
Реализация: выполнять у данных (объектов в одной иерархии классов) dynamic_cast<>(). Аналогичная задача в COM выполняется через QueryInterface().
3. Данные разнородны — это могут быть и объекты, и скалярные, и еще какие-то. В каждом случае их обрабатывают по-разному.
Реализация: union возможных типов + тэг типа. Пример — структура VARIANT и весь API к нему.
4. Каждому типу данных соответствует некоторый (шаблонный) способ обработки.
Реализация: делаем опосредование (паттерн Прокси).
struct IElem
{
virtual ~IElem() {}
... какие действия мы будем совершать над данным ...
};
class Proxy
{
IElem* elem_;
public:
template<class T>
Proxy(const T& v) : elem_(make_elem(v)) ...{...}
};
// make_elem() - порождает объект-манипулятор с интерфейсом IElem. Например такой:template<class T>
class ElemImp : public IElem
{
T v_;
public:
ElemImp(const T& v) : v_(v) ... {...}
...
};
template<class T> ElemImp<T>* make_elem(const T& v) { return new ElemImp<T>(v); }
// могут существовать отдельные специализации как для ElemImp так и для make_elem().
Примерно по такой схеме сделано связывание функторов в boost и Loki. (Там, правда, гораздо большее опосредование, но это уже детали).
Здравствуйте, Кодт, Вы писали: Al_>>Хочется (:, чтобы экземпляры класса могли хранить указатели разных типов, а, методы класса, хотябы, возвращать эти указатели.
К>Уточни пожалуйста, что ты хочешь. К>* чтобы для одного и того же класса (а не шаблона классов!) разные экземпляры хранили указатели разных типов
Это более подходит. Лучше примером покажу (как _должно_ работать):
Особо подчеркну, что требуется, чтобы метод возвращал указатель нужного типа без всяких преобразований.
При этом теоретически в классе не должно требоваться описание одной и той же функции для различных указателей. (можно ли в таком случае обойтись без шаблонных функций? А то, согласись, — неудобно писать quInt.GetItem<int*>(); )
К>Последний случай — тривиальный. Просто делаешь все что нужно шаблоном. Можно еще сделать функции-фабрики для автоматического вывода типа, наподобие такого: К>
К>А вот с первым — придется повозиться. Тут многое зависит от того, что именно и на какой стадии (компиляция, исполнение) ты хочешь делать с данными разных типов.
Важное замечание: речь не о данных, а об указателях. Главная цель — чтоб отладчик различал указатели.
К>Перечислю некоторые возможности.
К>1. Данные — это объекты с одним и тем же интерфейсом.
Данные — указатели на данные (: К>Реализация: делаем методы интерфейса виртуальными, наследуем объекты от него, обращаемся к данным через этот интерфейс. К>Все просто и естественно.
Здравствуйте, Al_, Вы писали:
Al_>Хочется (:, чтобы экземпляры класса могли хранить указатели разных типов, а, методы класса, хотябы, возвращать эти указатели. Al_> Типа такого:
Al_>
Здравствуйте, Sergeem, Вы писали:
Al_>>Хочется (:, чтобы экземпляры класса могли хранить указатели разных типов, а, методы класса, хотябы, возвращать эти указатели.
Al_>>Можно-ли подобное реализовать?
S>В этом случае действительно намонго приятнее пользоваться готовыми контейнерами из STL. S>Примерно так:
S>
S>Смотри, мой класс получился короче,
Наверно ты прав, но у меня рука не поднимится убить написанный мной класс, который не содержит ничего лишнего и полностью удовлетворяет мои потребности (:
Тем более я не сторонник использования готовых библиотек. Наверно в этом я не прав, раз все пользуются ими, но пока ничего делать с собой не хочу (:
S>к тому же не надо вспомогательных классов типа CQueueItem
У тебя вместо него std::list<TDataType>. Он лучше чем-то?
Здравствуйте, Al_, Вы писали:
S>>Смотри, мой класс получился короче, Al_> Наверно ты прав, но у меня рука не поднимится убить написанный мной класс, который не содержит ничего лишнего и полностью удовлетворяет мои потребности (: Al_> Тем более я не сторонник использования готовых библиотек. Наверно в этом я не прав, раз все пользуются ими, но пока ничего делать с собой не хочу (:
10 правил программирования. Вторая или третья: Воруйте код.
(с) Джон Бентли. Жемчужины программирования
Ты предпочитаешь делать работу, которая за тебя уже давно сделана. Причем лучше чем ты можешь сделать.
S>>к тому же не надо вспомогательных классов типа CQueueItem Al_>У тебя вместо него std::list<TDataType>. Он лучше чем-то?
Он
1. стандартный
2. не содержит ошибок
3. оптимизирован
4. легок в восприятии
5. уже написан
Al_>Особо подчеркну, что требуется, чтобы метод возвращал указатель нужного типа без всяких преобразований. Al_>При этом теоретически в классе не должно требоваться описание одной и той же функции для различных указателей. (можно ли в таком случае обойтись без шаблонных функций? А то, согласись, — неудобно писать quInt.GetItem<int*>(); )
Дак и будет работать.
Только, похоже, ты еще не определился с тем, что и как хранить, пихать и использовать.
Надо быть проще. Указывай для очереди тип элемента (будь то указатель, число, объект по значению). И методы доступа тоже должны работать с ним, а не с указателем-на-него.
template<class E>
class CQueue
{
public:
...
void AddItem(const E& e);
void DropItem(); // уничтожает первый элемент
E& PeekItem() const; // первый элемент - может быть доступен по RW-ссылке
E GetItem(); // вынимает из очереди - элемент должен быть скопирован, поскольку место его хранения уничтожится
};
// определения членовtemplate<class E>
void CQueue<E>::AddItem(const E& e)
{ . . . }
template<class E>
void CQueue<E>::DropItem()
{ . . . }
template<class E>
E& CQueue<E>::PeekItem() const
{ . . . }
template<class E>
E CQueue<E>::GetItem()
{ E temp; DropItem(); return temp; }
/////////////////////////////////
// использование
...
CQueue<int> qInts;
qInts.AddItem(5);
qInts.PeekItem() *= 2;
int n = qInts.GetItem(); // получили 10 :)
CQueue<CObject*> qPtrs;
qPtrs.AddItem(new CDerivedObject);
CObject* p = qPtrs.GetItem();
Если предполагается, что очередь содержит указатели на объекты, и известна политика их создания/удаления (например, деструктор очереди должен удалить все, что в ней лежит) — лучше сделай адаптер.
Так ты разнесешь функциональность очереди вообще и специфики указателей.
К примеру:
template<class T>
class PtrQueue : public Queue< boost::shared_ptr<T> > // shared_ptr обеспечит всю возню с жизнью и смертью данных
{
typedef Queue< boost::shared_ptr<T> > Base;
public:
void AddItem(boost::shared_ptr<T> const& p) { Base::AddItem(p); }
void AddItem(T* p) { Base::AddItem(p); }
void AddItem(T const& t) { Base::AddItem(new T(t)); } // бонус: можно передавать данные для конструктора копирования
};
Здравствуйте, Al_, Вы писали:
S>>Смотри, мой класс получился короче, Al_> Наверно ты прав, но у меня рука не поднимится убить написанный мной класс, который не содержит ничего лишнего и полностью удовлетворяет мои потребности (: Al_> Тем более я не сторонник использования готовых библиотек. Наверно в этом я не прав, раз все пользуются ими, но пока ничего делать с собой не хочу (:
S>>к тому же не надо вспомогательных классов типа CQueueItem Al_>У тебя вместо него std::list<TDataType>. Он лучше чем-то?
Воспользоваться библиотекой STL (которая входит в Стандарт С++!) не только не зазорно, но и полезно.
А насчет убийства собственного класса... Сохрани фасад (т.е. интерфейс для кода, который им пользуется), а внутри сделай евроремонт
Здравствуйте, Al_, Вы писали:
Al_> Наверно ты прав, но у меня рука не поднимится убить написанный мной класс, который не содержит ничего лишнего и полностью удовлетворяет мои потребности (:
он содержит баги, которые ты потом замучаешься исправлять и из-за этого потом
все бросишь на пол-пути.
Al_> Тем более я не сторонник использования готовых библиотек. Наверно в этом я не прав, раз все пользуются ими, но пока ничего делать с собой не хочу (:
Со временем придет Старайся идти проверенным путем вместо того чтобы пробивать
тропинку самому, наступая на грабли, которые уже стукнуди по лбу не один десяток программистов.
S>>к тому же не надо вспомогательных классов типа CQueueItem Al_>У тебя вместо него std::list<TDataType>. Он лучше чем-то?
например он лучше тем, что он стандартный. т.е. если кто-то другой будет смотреть твой код
то ему будет проще разобраться.
есть еще много других аргументов, советую прочитать какую-нибудь книжку по stl.
Serge.
Hасколько проще была бы жизнь, если бы она была в исходниках.
Здравствуйте, Aera, Вы писали:
A>10 правил программирования. Вторая или третья: Воруйте код. A>(с) Джон Бентли. Жемчужины программирования
(%
A>Ты предпочитаешь делать работу, которая за тебя уже давно сделана. Причем лучше чем ты можешь сделать.
К слову: Я не люблю MFC. И я такой не один. (немного от темы, но ничего...).
S>>>к тому же не надо вспомогательных классов типа CQueueItem Al_>>У тебя вместо него std::list<TDataType>. Он лучше чем-то?
A>Он A>1. стандартный A>2. не содержит ошибок A>3. оптимизирован A>4. легок в восприятии A>5. уже написан
6. Универсален.
А гибкость обычно идет противоречием с производительностью. Обычно...
Al_>>CQueue<int*> quInt;
Al_>>quInt.AddItem(5); // -----опечатка - тут надо адрес давать---- (:
Al_>>int i = *quInt.GetItem(); // Возвращает int*
Al_>>.....
Al_>>CQueue<CObject*> quObj;
Al_>>quObj.AddItem(new CObject);
Al_>>CObject* pObj = quObj.GetItem(); // Возвращает CObject*
Al_>>
К>Только, похоже, ты еще не определился с тем, что и как хранить, пихать и использовать. К>Надо быть проще. Указывай для очереди тип элемента (будь то указатель, число, объект по значению). И методы доступа тоже должны работать с ним, а не с указателем-на-него.
Видимо опечатка натолкнула тебя на эти философские рассуждения (% Работать конечно надо с объектом (для которого создана очередь), а не с указателем на него. Нельзя не согласиться.
К>
К>template<class E>
К>class CQueue
К>{
К>public:
К> ...
К> void AddItem(const E& e);
К> void DropItem(); // уничтожает первый элемент
К> E& PeekItem() const; // первый элемент - может быть доступен по RW-ссылке
К> E GetItem(); // вынимает из очереди
К>};
>template<class E>
К>E CQueue<E>::GetItem()
К>{ E temp; DropItem(); return temp; }
Это как работает? ^ temp ведь не иницилизируется?! ************************************
К>/////////////////////////////////
К>// использование
К>...
К>CQueue<int> qInts;
К>qInts.AddItem(5);
К>qInts.PeekItem() *= 2;
К>int n = qInts.GetItem(); // получили 10 :)
К>CQueue<CObject*> qPtrs;
К>qPtrs.AddItem(new CDerivedObject);
К>CObject* p = qPtrs.GetItem();
К>
К>Если предполагается, что очередь содержит указатели на объекты, и известна политика их создания/удаления (например, деструктор очереди должен удалить все, что в ней лежит) — лучше сделай адаптер. К>Так ты разнесешь функциональность очереди вообще и специфики указателей.
А для этого разве недостаточено просто написать в деструкторе CQueueItem:
delete pData
? (pData определен в этом классе как TT pData). При этом если TT будет классом, то вызовется его деструктор.
Здравствуйте, Кодт, Вы писали:
S>>>к тому же не надо вспомогательных классов типа CQueueItem Al_>>У тебя вместо него std::list<TDataType>. Он лучше чем-то?
К>Воспользоваться библиотекой STL (которая входит в Стандарт С++!) не только не зазорно, но и полезно. К>А насчет убийства собственного класса... Сохрани фасад (т.е. интерфейс для кода, который им пользуется), а внутри сделай евроремонт
Там столько делать придется. Он у меня уже не очень маленький да еще и постоянно растет. (%
Да и на изучение STL время тоже надо.
Здравствуйте, Sergeem, Вы писали:
Al_>> Наверно ты прав, но у меня рука не поднимится убить написанный мной класс, который не содержит ничего лишнего и полностью удовлетворяет мои потребности (:
S>он содержит баги, которые ты потом замучаешься исправлять и из-за этого потом S>все бросишь на пол-пути.
Да нету там багов. Уже столько его использую и все прекрасно работает. Вот захотел только добавить полиморфности, и тут его засра... (:
Al_>> Тем более я не сторонник использования готовых библиотек. Наверно в этом я не прав, раз все пользуются ими, но пока ничего делать с собой не хочу (:
S>Со временем придет Старайся идти проверенным путем вместо того чтобы пробивать S>тропинку самому, наступая на грабли, которые уже стукнуди по лбу не один десяток программистов.
Да тут воощемто особо никаких граблей и нет. Все давольно-таки тривиально. Только с шаблонами не знал как управиться.
Al_>>У тебя вместо него std::list<TDataType>. Он лучше чем-то? S>например он лучше тем, что он стандартный. т.е. если кто-то другой будет смотреть твой код то ему будет проще разобраться. S>есть еще много других аргументов, советую прочитать какую-нибудь книжку по stl.
Обязательно посмортю.
Al_>>>CQueue<int*> quInt;
Al_>>>quInt.AddItem(5); // -----опечатка - тут надо адрес давать---- (:
Al_>>>int i = *quInt.GetItem(); // Возвращает int*
Al_>>>
<> Al_>Видимо опечатка натолкнула тебя на эти философские рассуждения (% Работать конечно надо с объектом (для которого создана очередь), а не с указателем на него. Нельзя не согласиться.
Ну да. А еще — вот это: i=*(quInt.GetItem()); Я так понимаю — это мы выпихиваем элемент из очереди, следовательно, прекращаем владение данными, на которые ссылается указатель. После разыменования мы копируем данные в переменную, а что делаем с оригиналом? Если это был new int(5) — получаем утечку памяти.
К>>
>>template<class E>
К>>E CQueue<E>::GetItem()
К>>{ E temp; DropItem(); return temp; }
Al_>// Это как работает? ^ temp ведь не иницилизируется?! ************************************
// это я поторопился. Правильно так:
{ E temp = PeekItem(); DropItem(); return temp; }
К>>
К>>Если предполагается, что очередь содержит указатели на объекты, и известна политика их создания/удаления (например, деструктор очереди должен удалить все, что в ней лежит) — лучше сделай адаптер. К>>Так ты разнесешь функциональность очереди вообще и специфики указателей. Al_>А для этого разве недостаточено просто написать в деструкторе CQueueItem:
delete pData
? (pData определен в этом классе как TT pData). При этом если TT будет классом, то вызовется его деструктор.
Не только недостаточно, но и избыточно!!! CQueueItem разрушается в ходе выполнения GetItem(). Спрашивается: что мы вернем оттуда — висячий указатель на свежеубитый объект?
А с другой стороны, если мы хотим просто выпихнуть элемент из очереди... Нужно писать delete theQueue.GetItem();
Чтобы не забивать голову с управлением — нужно хранить в очереди умные указатели std::auto_ptr<T>, boost::shared_ptr<T>, Loki::SmartPtr<T>.
Здравствуйте, Al_, Вы писали:
К>>Воспользоваться библиотекой STL (которая входит в Стандарт С++!) не только не зазорно, но и полезно. К>>А насчет убийства собственного класса... Сохрани фасад (т.е. интерфейс для кода, который им пользуется), а внутри сделай евроремонт Al_>Там столько делать придется. Он у меня уже не очень маленький да еще и постоянно растет. (% Al_>Да и на изучение STL время тоже надо.
"Лучше день потерять, а потом за час долететь" (с) Ноги, крылья и хвосты.
Здравствуйте, Al_, Вы писали:
Al_>Здравствуйте, Aera, Вы писали:
A>>Он A>>1. стандартный A>>2. не содержит ошибок A>>3. оптимизирован A>>4. легок в восприятии A>>5. уже написан Al_>6. Универсален. Al_>А гибкость обычно идет противоречием с производительностью. Обычно...
Верно ты с STL плохо знаком. Тебе ж написали: 3. оптимизирован!
Serge.
Hасколько проще была бы жизнь, если бы она была в исходниках.