18 ноября 2008

The magic of shared_ptr

В качестве еще одной иллюстрации к предыдущей заметке про "магию shared_ptr", в ответ один из комментариев - небольшой пример: создадим простую иерархию классов, где базовый класс не имеет виртуального деструктора.
struct A { ~A() { cout << "~A()" << endl; } };
struct B : public A { ~B() { cout << "~B()" << endl; } };
А теперь создадим объект класса B и поместим его в контейнер указателей на A:
{
auto_ptr<B> b(new B);
ptr_vector<A> x;
x.push_back(b);
}

{
auto_ptr<B> b(new B);
vector< shared_ptr<A> > x;
x.push_back(b);
}
В случае контейнера, хранящего только указатели (в данном случае - boost::ptr_vector) - получим при разрушении объекта вызов только ~A(), а в случае с shared_ptr будет вызван ~B(), так как shared_ptr немного умнее обычного указателя.

17 ноября 2008

Storing dynamicly created objects in a container

Хранение полиморфных объектов в контейнере - классика жанра:
class A { ... };
class B : public A { ... };
std::vector<A*> BunchOfObjects;
BunchOfObjects.push_back(new B);
Главное - не забыть уничтожить удаляемые из контейнера объекты. И если делать это через оператор delete - еще про виртуальный деструктор у базового класса не забыть бы.

Хотя... другое классическое решение позволяет обойтись и без первого и без второго:
std::vector< boost::shared_ptr<A> > BunchOfObjects;
BunchOfObjects.push_back(boost::shared_ptr<A>(new B));
Наличие виртуального деструктора в таком случае не принципиально - shared_ptr запоминает как нужно удалять объект, по умолчанию это будет оператор delete для нужного типа. Таким образом можно хранить, например, список динамически созданных окон - предков ATL::CWindow. Более того, если от хранимых объектов нужна лишь возможность их в нужный момент удалить, то можно вообще не задумываться от их типе используя boost::shared_ptr<void>.

13 ноября 2008

Wake me up tomorrow

Функции WinAPI, которые можно использовать в качестве "будильника" - SetTimer(), WaitForSingleObject(), и прочие принимают время относительное, а не абсолютное. Нельзя сказать "Разбудите меня завтра!". Только "Пните меня через 13786 миллисекунд".

Разумный вариант - подсчитать сколько миллисекунд до заданного времени, но ничто не вечно под луной. Время может перевести пользователь, программка, синхронизирующаяся с сервером, да сама Windows, в конце концов. Так что заодно мониторим WM_TIMECHANGE и пересчитываем сколько осталось.
void SetTimerToTommorrow()
{
using namespace boost::posix_time;
using namespace boost::gregorian;

ptime Now(second_clock::local_time()), Tomorrow(Now.date() + days(1));
SetTimer(..., (Tomorrow - Now).total_milliseconds() + 1, ...);
}