}
private:
HDC _hdc;
HPEN _hPen;
HPEN _hPenOld;
};
Точно таким же способом Вы можете работать с кистями (кистей, поддерживаемых Windows, гораздо больше, чем перьев). В качестве примера, ниже дается определение ColorBrush.
class ColorBrush {
public:
ColorBrush(HDC hdc, COLORREF color) : _hdc (hdc) {
_hBrush = CreateSolidBrush(color);
_hBrushOld = (HBRUSH)SelectObject(_hdc, _hBrush);
}
~ColorBrush() {
SelectObject(_hdc, _hBrushOld);
DeleteObject(_hBrush);
}
private:
HDC _hdc;
HBRUSH _hBrush;
HBRUSH _hBrushOld;
};
Как всегда, мы пощряем Ваши собственные эксперименты.
Далее: совершенно иная тема — «Потоки».
Классовая оболочка для потоков
Перевод А. И. Легалова
Англоязычный оригинал находится на сервере компании Reliable Software
Многозадачность — один из наиболее трудных аспектов программирования. Поэтому, для нее тем более важно обеспечить простой набор абстракций и инкапсулировать их в хорошей объектно-ориентированной оболочке. В ОО мире, естественным аналогом потока, являющегося, чисто процедурной абстракцией, служит «Активный объект». Активный объект обладает удерживаемым потоком, который асинхронно выполняет некоторые задачи. Этот поток имеет доступ к всем внутренним (закрытым) данным и методам объекта. Открытый интерфейс Активного объекта доступен внешним агентам (таким как основному потоку, или потоку, несущему сообщения Windows). Поэтому, они могут манипулировать состоянием объекта также, как эта манипуляция осуществляется из удерживаемого потока. Хотя, режим управления при этом сильно ограничен.
Активный Объект сформирован как каркас по имени ActiveObject. Построение производного класса, как предполагается, обеспечивает реализацию для чистых виртуальных методов InitThread, Run и Flush (также как и написание деструктора).
class ActiveObject {
public:
ActiveObject();
virtual ~ActiveObject() {}
void Kill();
protected:
virtual void InitThread() = 0;
virtual void Run() = 0; virtual void FlushThread() = 0;
static DWORD WINAPI ThreadEntry(void *pArg);
int _isDying;
Thread _thread;
};
Конструктор класса ActiveObject инициализирует удерживаемый поток, передавая ему указатель функции, которую предполагается выполнить и указатель "this" на себя. Мы должны отключить предупреждение, сигнализирующее об использовании "this" до полного создания объекта. Мы знаем, что этот объект не будет использоваться раньше положенного, потому что поток создается в неактивном состоянии. Предполагается, сто конструктор производного класса вызывает _thread.Resume() чтобы активизировать поток.
// The constructor of the derived class
// should call
// _thread.Resume ();
// at the end of construction
ActiveObject::ActiveObject() : _isDying (0),
#pragma warning(disable: 4355) // 'this' used before initialized
_thread(ThreadEntry, this)
#pragma warning(default: 4355)
{ }
Метод Kill вызывает виртуальный метод FlushThread — это необходимо для завершения потока из любого состояния ожидания и дает ему возможность запустить _isDying для проверки флажка.
void ActiveObject::Kill() {
_isDying++;
FlushThread();
// Let's make sure it's gone
_thread.WaitForDeath();
}
Мы также имеем каркас для функции ThreadEntry (это — статический метод класса ActiveObject, поэтому мы можем определять соглашение о вызовах, требуемое API). Эта функция выполняется удерживаемым потоком. Параметр, получаемый потоком от системы является тем, который мы передали конструктору объекта потока — это указатель "this" Активного Объекта. API ожидает void-указатель, поэтому мы должны делать явное приведение указателя на ActiveObject. Как только мы овладеваем Активным Объектом, мы вызываем его чистый виртуальный метод InitThread, делать все специфические для реализации приготовления, а затем вызываем основной рабочий метод Run. Реализация метода Run оставлена клиенту каркаса.
DWORD WINAPI ActiveObject::ThreadEntry(void* pArg) {
ActiveObject* pActive = (ActiveObject*)pArg;
pActive->InitThread();
pActive->Run();
return 0;
}
Объект Thread — это тонкая инкапсуляция API. Обратите внимание на флажок CREATE_SUSPENDED, который гарантирует, что нить не начнет выполняться прежде, чем мы не закончим конструирование объекта ActiveObject.
class Thread {
public:
Thread(DWORD(WINAPI* pFun)(void* arg), void* pArg) {
_handle = CreateThread(
0, // Security attributes
0, // Stack size
pFun, pArg, CREATE_SUSPENDED, &_tid);
}
~Thread() {
CloseHandle(_handle);
}
void Resume() {
ResumeThread(_handle);
}
void WaitForDeath() {
WaitForSingleObject(_handle, 2000);
}
private:
HANDLE _handle;
DWORD _tid; // thread id
};
Синхронизация — это то, что действительно делает многозадачный режим столь интенсивно используемым. Давайте, начнем со взаимных исключений. Класс Mutex — тонкая инкапсуляция API. Вы внедряете Mutexes (мутации) в ваш Активный Объект, а затем используете их через Блокировки. Блокировка (Lock) — умный объект, который создается на стеке. В результате чего, во время обслуживания, ваш объект защищен от любых других потоков. Класс Lock — одно из приложений методологии Управления ресурсами. Вы должны поместить Lock внутри всех методов вашего Активного Объекта, которые разделяют доступ к данным с другими потоками.
class Mutex {
friend class Lock;
public:
Mutex() {
InitializeCriticalSection(&_critSection);
}
~Mutex() {
DeleteCriticalSection(&_critSection);
}
private:
void Acquire() {
EnterCriticalSection(&_critSection);
}
void Release() {
LeaveCriticalSection(&_critSection);