Задумался этим вечером, как страуструп и ко предлагают разруливать такие ситуации:
void MyFun()
{
std::vector<MyClass1*> v1;
std::list<MyClass2*> v2;
...
std::map<MyClassN*, MyClassN1*> vN;
//дальше идет тяжелая работа с коллекциями, где создаются и добавляются в эти коллекции экземпляры
//MyClass1..MyClassN, при этом в любой момент может выскочить исключение
}
при генерации исключения надо прибить все объекты, которые накопились в коллекциях
есть ли возможность это сделать без десятков строк идиотского кода ?
Здравствуйте, Karamat, Вы писали:
K>при генерации исключения надо прибить все объекты, которые накопились в коллекциях
K>есть ли возможность это сделать без десятков строк идиотского кода ?
Здравствуйте, Karamat, Вы писали:
K>при генерации исключения надо прибить все объекты, которые накопились в коллекциях
K>есть ли возможность это сделать без десятков строк идиотского кода ?
Здравствуйте, Karamat, Вы писали:
K>при генерации исключения надо прибить все объекты, которые накопились в коллекциях K>есть ли возможность это сделать без десятков строк идиотского кода ?
Использовать умные указатели.
Здравствуйте, watchyourinfo, Вы писали:
W>да, но нужно понимать, что это было невозможно до появления emplace_back и move семантики в стандарте
До этого были всякие boost::ptr_vector.
Здравствуйте, _hum_, Вы писали:
K>>при генерации исключения надо прибить все объекты, которые накопились в коллекциях
K>>есть ли возможность это сделать без десятков строк идиотского кода ?
__>
__>std::vector<std::unique_ptr<MyClass1> >
__>
__>?
в функции используется АПИ, требующее ссылок типа std::vector<MyClass1*>
Здравствуйте, Karamat, Вы писали:
K>при генерации исключения надо прибить все объекты, которые накопились в коллекциях K>есть ли возможность это сделать без десятков строк идиотского кода ?
Да, воспользуйся мощью автодеструкторов. Которая уже реализована в умных указателях.
Здравствуйте, Karamat, Вы писали:
K>Здравствуйте, _hum_, Вы писали:
K>>>при генерации исключения надо прибить все объекты, которые накопились в коллекциях
K>>>есть ли возможность это сделать без десятков строк идиотского кода ?
__>>
__>>std::vector<std::unique_ptr<MyClass1> >
__>>
__>>?
K>в функции используется АПИ, требующее ссылок типа std::vector<MyClass1*>
тогда, может, так попробовать:
template<typename value_type>
class vector_of_pointed_values: public std::vector<value_type*>
{
public:
//---virtual
~vector_of_pointed_values(void)
{
//<код удаления>
}
//---
};
Здравствуйте, Karamat, Вы писали: K>Здравствуйте, _hum_, Вы писали: K>>>при генерации исключения надо прибить все объекты, которые накопились в коллекциях K>>>есть ли возможность это сделать без десятков строк идиотского кода ?
__>>
__>>std::vector<std::unique_ptr<MyClass1> >
__>>
__>>? K>в функции используется АПИ, требующее ссылок типа std::vector<MyClass1*>
Здравствуйте, DarkEld3r, Вы писали:
W>>да, но нужно понимать, что это было невозможно до появления emplace_back и move семантики в стандарте DE>До этого были всякие boost::ptr_vector.
Здравствуйте, Karamat, Вы писали:
K>Код удаления не просто delete p, перед этим надо еще что-то сделать, для каждого типа свое, в зависимости от значений локальных переменных:
Мне одному кажется, что ты на ходу досочиняешь требования?
Можно сразу весь список огласить?
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, _hum_, Вы писали:
__>>тогда, может, так попробовать: __>> class vector_of_pointed_values: public std::vector<value_type*>
К>Наследоваться от стандартных контейнеров — моветон. К>Лучше уж тогда умные указатели на контейнеры:
К>http://ideone.com/vSlz1i
К>
ну, а если кто-то сделает два клинера одного объекта, или не отпустит, или отпустит раньше времени и т.п.
все-таки ооп в первую очередь и задумывалось для того, чтобы не разбрасывать по коду объект и действия над ним, дабы максимально уменьшить вероятность подобных ошибок.
Здравствуйте, _hum_, Вы писали:
__>ну, а если кто-то сделает два клинера одного объекта, или не отпустит, или отпустит раньше времени и т.п.
или забудет написать finally, или возьмёт невладеющий контейнер вместо владеющего, и т.п.
__>все-таки ооп в первую очередь и задумывалось для того, чтобы не разбрасывать по коду объект и действия над ним, дабы максимально уменьшить вероятность подобных ошибок.
Это не вполне вопрос ООП.
Напротив, здесь больше пахнет ФП, т.к. по месту выполняется слияние трёх задач
— хранение коллекции
— управление временем жизни элементов
— реакция на коммит/ролбек
Если сценарий "хранить и убивать элементы при смерти контейнера" не одноразовый, — то нужны соответсвующие контейнеры. Тот же boost::ptr_vector.
И желательно протащить их в интерфейс, чтоб принимающая сторона тоже работала с ними.
А если именно по месту, то — либо писать финализатор (заделывать прореху синтаксиса С++), либо RAII-обёртку.
А в рамках ООП — наследоваться от стандартных контейнеров — моветон.
Потому что они не заточены на полиморфизм времени исполнения. И писать "virtual ~my_derived_vector()" — это обманывать сам себя.
Но если не отдавать такие контейнеры на сторону (и гарантировать это!) то можно сплавить хранение и удаление в один флакон через наследование. Но лучше (имхо) всё же через обёртку.
Кстати, коммит (лучше) делать явно, а не как противодействие ролбеку:
Здравствуйте, jazzer, Вы писали:
K>>Код удаления не просто delete p, перед этим надо еще что-то сделать, для каждого типа свое, в зависимости от значений локальных переменных:
J>Мне одному кажется, что ты на ходу досочиняешь требования?
В данном конкретном случае досочинил про локальные переменные, просто потому, что в перспективе могут быть нужны и они.
J>Можно сразу весь список огласить?
Вроде все — есть несколько коллекций разных типов, каждый элемент которых нужно в конце функции обработать, вне зависимости от того, вылетело ли исключение или нет.
Пока я вижу два пути
1. от товарища _hum_ — сделать для каждой коллекции класс наследник, в деструктор засунуть обработку. 10 коллекций — надо 10 классов сделать вместо слова finally и двух скобок, а если нужны локальные переменные то решение вообще не работает.
2. от so5team — через лямбды, оно похоже на правду, но уж больно дико выглядит с непривычки к 11 стандарту, боюсь неожиданных спецэффектов.
Здравствуйте, Karamat, Вы писали:
K>Задумался этим вечером, как страуструп и ко предлагают разруливать такие ситуации:
K>
K>void MyFun()
K>{
K> std::vector<MyClass1*> v1;
K> std::list<MyClass2*> v2;
K> ...
K> std::map<MyClassN*, MyClassN1*> vN;
K> //дальше идет тяжелая работа с коллекциями, где создаются и добавляются в эти коллекции экземпляры
K> //MyClass1..MyClassN, при этом в любой момент может выскочить исключение
K>}
K>
K>при генерации исключения надо прибить все объекты, которые накопились в коллекциях
K>есть ли возможность это сделать без десятков строк идиотского кода ?
По приведённому примеру кода не видно, и явно не указано требование, чтобы объекты в коллекциях были полиморфными. C++11 поддерживает семантику перемещения, так что добавление и удаление элементов в контейнере может быть и не таким тяжелым сценарием, как это представляется в теории. А профайлер покажет, так ли оно на практике. Используйте value типы, там где это возможно, обычно это упрощает код:
void MyFun()
{
std::vector<MyClass1> v1;
// и никаких сложностей с ручным управлением памятью
// зато получаем локальность данных в случае с std::vector
}
Здравствуйте, PM, Вы писали:
PM>По приведённому примеру кода не видно, и явно не указано требование, чтобы объекты в коллекциях были полиморфными.
там все гораздо хуже
в функции используется АПИ, требующее ссылок типа std::vector<MyClass1*>
PM>Используйте value типы, там где это возможно, обычно это упрощает код
Там по настоящему более общая задача — в функции надо гарантированно выполнить определенные действия, даже если возникнет исключение. Поэтому не важно, указатель или value. Для такого придумали finally, а в С++, на мой взгляд, минимальный оверхед будет при таком коде:
void FunA(int a, int b, int c)
{
try
{
//код функции
}
catch(...)
{
FunAFinally(a, b, c);
throw;
}
FunAFinally(a, b, c);
}
void FunAFinally(int a, int b, int c)
{
//код финализатора
}