Т.к. классов команд несколько, приходится хранить в очереди не сами объекты, а указатели на них. CommandExecutor после выборки команды из очереди и ее исполнения просто уничтожает соответствующий объект. Получается, что для передачи команды в CommandExecutor::enqueueCommand необходимо создать объект конкретной команды в куче. Собственно ничего не мешает это сделать, но хочется писать код, который сложно использовать неправильно. В данном случае придется откомментировать enqueueCommand, обозначив необходимость создания объектов Command в куче и переход права владения на объект к CommandExecutor. Ну а теперь собственно сам вопрос: как лучше решается такая проблема? Исключительно комментированием условий использования или все же можно сделать реализацию поизящнее?
Мне на ум приходит разве что использование pimpl для Command. Но в этом случае придется создавать кучу дополнительного кода для каждого конкретного класса-команды. Так же можно воспользоваться boost::any, но очень не хочется добавлять дополнительные накладные расходы, т.к. код довольно критичен ко времени выполнения.
Здравствуйте, theCreature, Вы писали:
C>Т.к. классов команд несколько, приходится хранить в очереди не сами объекты, а указатели на них. CommandExecutor после выборки команды из очереди и ее исполнения просто уничтожает соответствующий объект. Ну а теперь собственно сам вопрос: как лучше решается такая проблема? Исключительно комментированием условий использования или все же можно сделать реализацию поизящнее?
Я обычно методы принимающие владение оформляю так:
void TakeOwnership(std::auto_ptr<T> object); // Принимает владение
Здравствуйте, theCreature, Вы писали:
C>Т.к. классов команд несколько, приходится хранить в очереди не сами объекты, а указатели на них. CommandExecutor после выборки команды из очереди и ее исполнения просто уничтожает соответствующий объект. Получается, что для передачи команды в CommandExecutor::enqueueCommand необходимо создать объект конкретной команды в куче. Собственно ничего не мешает это сделать, но хочется писать код, который сложно использовать неправильно. В данном случае придется откомментировать enqueueCommand, обозначив необходимость создания объектов Command в куче и переход права владения на объект к CommandExecutor. Ну а теперь собственно сам вопрос: как лучше решается такая проблема? Исключительно комментированием условий использования или все же можно сделать реализацию поизящнее? C>Мне на ум приходит разве что использование pimpl для Command. Но в этом случае придется создавать кучу дополнительного кода для каждого конкретного класса-команды. Так же можно воспользоваться boost::any, но очень не хочется добавлять дополнительные накладные расходы, т.к. код довольно критичен ко времени выполнения.
Так для полиморфного использования в любом случае придётся пользоваться указаелями, но наверное лучше использовать unique_ptr из нового стандарта. Что будет говорить, что объект имеет политику одиночного владения.
А Command разрешть создавать только фабрике, которая будет отдавать unique_ptr<Command>.
Здравствуйте, Ytz, Вы писали:
Ytz>Здравствуйте, _niko_, Вы писали:
__>> — деструктор абстрактным быть не может.
Ytz>Еще как может. Более того так часто делают специально для абстрактных базовых классов.
В первом случае (Class1) деструктор создан не будет, т.к. мы его не определили а компилятору он не нужен (соответственно об абстрактности деструктора говорить не приходится).
Во втором случае (Class2) объявляете вы деструктор или не объявляете или делаете его виртуальным и абстрактным — он будет создан в любом случае, т.к. есть внутренние объекты которые необходимо удалять.
Re[2]: Хранение в контейнере объектов разного типа
Здравствуйте, theCreature, Вы писали:
C>Имеется класс команда и куча производных от него конкретных команд:
[здесь был пример кода]
C>Т.к. классов команд несколько, приходится хранить в очереди не сами объекты, а указатели на них. CommandExecutor после выборки команды из очереди и ее исполнения просто уничтожает соответствующий объект. Получается, что для передачи команды в CommandExecutor::enqueueCommand необходимо создать объект конкретной команды в куче. Собственно ничего не мешает это сделать, но хочется писать код, который сложно использовать неправильно. В данном случае придется откомментировать enqueueCommand, обозначив необходимость создания объектов Command в куче и переход права владения на объект к CommandExecutor. Ну а теперь собственно сам вопрос: как лучше решается такая проблема? Исключительно комментированием условий использования или все же можно сделать реализацию поизящнее? C>Мне на ум приходит разве что использование pimpl для Command. Но в этом случае придется создавать кучу дополнительного кода для каждого конкретного класса-команды. Так же можно воспользоваться boost::any, но очень не хочется добавлять дополнительные накладные расходы, т.к. код довольно критичен ко времени выполнения.
Про boost еще вроде не упоминали, отмечусь
Для хранения команд использовать boost::ptr_deque (или другой подходящий из Boost Pointer Container), для факта передачи владения — std::auto_ptr
Кстати, я думаю что опасения по поводу неверного использования кода слегка преувеличены — достаточно указать в комментариях к CommandExecutor что он владеет объектами Command (а лучше попробовать дать ему имя, говорящее об этом ). Адекватный программист не будет запихивать в CommandExecutor адреса стековых объектов, ну а неадекватный когда-нибудь получит испорченный стек.
Еще как вариант можно сделать фабрику объектов команд, но могут возникнуть сложности с передачей парамтеров в конструкторы производных классов.
Re[3]: Хранение в контейнере объектов разного типа
Здравствуйте, PM, Вы писали:
PM>как уже сказали, деструктор абстрактным быть может, нужно только реализовать его (не inline!):
Ответьте на вопрос: что такое абстрактный метод класса?
— это метод класса, реализация для которого отсутствует.
Да, в С++ можно для абстрактного метода сделать реализацию, но это тонкое место этого языка программирования (если хотите бага/фича) и закладываться на это при реализации какого то класса не правильно.
Посему нужно понимать что если метод объявлен как абстрактный — реализации в этом классе у него нет, но при наследовании нам необходимо её сделать.
Делая деструктор класса абстрактным мы обязуем наследника сделать его реализацию.
Но базовый класс не должен требовать от наследника реализовывать свой деструктор!
Ведь наследник может и не иметь ни одного внутреннего объекта/переменной, зачем ему тогда реализация деструктора?
Re[2]: Хранение в контейнере объектов разного типа
Здравствуйте, _niko_, Вы писали:
__> — деструктор абстрактным быть не может.
Ок, с аргументами, а не только с минусами. Открываем C++ Standart ISO-IEC 14882.1998(E) (у меня свежее нет пока), глава 12.4 Destructors, стих 7:
12.4 Destructors
...
7 A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any
derived class are created in the program, the destructor shall be defined. If a class has a base class with a
virtual destructor, its destructor (whether user- or implicitly- declared) is virtual.
Re[2]: Хранение в контейнере объектов разного типа
Здравствуйте, PM, Вы писали:
PM>Еще как вариант можно сделать фабрику объектов команд, но могут возникнуть сложности с передачей парамтеров в конструкторы производных классов.
Здравствуйте, PM, Вы писали:
PM>Здравствуйте, _niko_, Вы писали:
__>> — деструктор абстрактным быть не может.
PM>Ок, с аргументами, а не только с минусами. Открываем C++ Standart ISO-IEC 14882.1998(E) (у меня свежее нет пока), глава 12.4 Destructors, стих 7:
PM>
PM>12.4 Destructors
PM>...
PM>7 A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any
PM>derived class are created in the program, the destructor shall be defined. If a class has a base class with a
PM>virtual destructor, its destructor (whether user- or implicitly- declared) is virtual.
Нашел этот пункт в ISO/IEC 14882:2011(E): 12.4.9
Ну тут уже возразить нечего
Re[2]: Хранение в контейнере объектов разного типа
Здравствуйте, 13akaEagle, Вы писали: E>Так для полиморфного использования в любом случае придётся пользоваться указаелями, но наверное лучше использовать unique_ptr из нового стандарта. Что будет говорить, что объект имеет политику одиночного владения. E>А Command разрешть создавать только фабрике, которая будет отдавать unique_ptr<Command>.
Я думал насчет фабрики, но проблема еще в том, что наследники Command имеют конструкторы с различным количеством аргументов.
Re[3]: Хранение в контейнере объектов разного типа
Здравствуйте, theCreature, Вы писали:
C>Я думал насчет фабрики, но проблема еще в том, что наследники Command имеют конструкторы с различным количеством аргументов.
А так? http://codepad.org/N4UtpPMa
Re: Хранение в контейнере объектов разного типа
От:
Аноним
Дата:
24.11.11 10:04
Оценка:
Здравствуйте, theCreature, Вы писали:
...
А если как-то так?
class Command
{
protected:
virtual ~Command() = 0;
public:
virtual void Destroy() = 0;
};
class HeapCommand : public Command
{
public:
virtual void Destroy() { delete this; }
};
class StaticCommand : public Command
{
public:
virtual void Destroy() {}
};
Re[4]: Хранение в контейнере объектов разного типа
Здравствуйте, 13akaEagle, Вы писали: E>Смотрел boost::make_shared, пока не разобрался что там творится...
boost::make_shared создает объект вместе со счётчиком за один вызов new. Т.е. счётчик и объект лежат рядом.
Из очевидных минусов — пока существует хоть один weak pointer, смотрящий на уже освобождённый объект — память освобождена не будет.
Чтоб не плодить темы, у меня похожая задача.
Есть такой класс:
class Queue
{
public:
void AddCommand(void* lpObj, size_t sizeObj);
private:
void SucceedSend()
{
_queue.clear();
}
struct Command
{
void* lpObj;
size_t sizeObj;
~Command()
{
// Тут нужно удалить отправленный объект
// Такая конструкция само собой не подайдет delete lpObj;
// Как быть
}
};
std::list<Command> _queue;
};
Это класс в другой нитке оправляет сообщение по мере возможностей и после чистит очередь. Мне нужно удалять отправленные обькты как быть ?
Re[3]: Хранение в контейнере объектов разного типа