и семантики об работчиков завершения.
Мы уже далеко продвинулись в рассмотрении базового синтаксиса и семантики об работчиков завершения. Теперь поговорим о том, как обработчики завершения упро щают более сложные задачи программирования. Взгляните на функцию, в которой не используются преимущества обработки завершения:
BOOL Funcarama1()
{
HANDLE hFile = INVALID_HANDLE_VALUE;
PVOID pvBuf = NULL;
DWORD dwNumBytesRead;
BOOL fOk;
hFile = CreateFile("SOMEDATA.DAT", GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
lf (hFile == INVALID_HANDLE_VALUE)
{
return(FALSE);
}
pvBuf = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRTTE);
if (pvBuf == NULL)
{
CloseHandle(hFile);
return(FALSE);
}
fOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRead, NULL);
if (!fOk || (dwNumBytesRead == 0))
{
VirtualFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);
CloseHandle(hFile);
return(FALSE);
}
// что-то делаем с данными
...
// очистка всех ресурсов
VirtuallFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);
CloseHandle{hFile); return(TRUE);
}
Проверки ошибок в функции Fипсаrата1 затрудняют чтение ее текста, что услож няст ее понимание, сопровождение и модификацию
Конечно, можно переписать Funcaramal так, чтобы она была яснее:
BOOL Funcarama2()
{
HANDLE hFile = INVALID_HANDLE_VALUE;
PVOID pvBuf = NULL;
DWORD dwNumByTesRead;
BOOL fOk;
fSuccess = FALSE;
hFile = CreatcFile("SOMEDATA.DAT", GENERIC_READ, FILE_SHARE_READ NULL, OPEN_EXISTING, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
pvBuf = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (pvBuf != NULL)
{
fOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRedd, NULL);
if (fOk && (dwNumBytesRead != 0))
{
// что-то делаем с данными
...
fSuccess = TRUE;
}
}
VirtualFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);
}
CloseHandle(hFile);
return(fSuccess);
}
Funcarama2 легче для понимания, но по-прежнему трудна для модификации и сопровождения. Кроме того, приходится делать слишком много отступов по мере добавления новых условных операторов, после такой переделки Вы того и гляди нач нете писать код на правом краю экране и переносить операторы на другую строку через каждые пять символов!
Перспишем- ка еще раз первый вариант (Funcaramal), задействовав преимущества обработки завершения
BOOL Funcarama3()
{
// Внимание! Инициализируйте все переменные, предполагая худшее
HANDLE hFile = INVALID_HANDLE_VALUE;
PVOID pvBuf = NULL;
__try
{
DWORD dwNumBytesRead;
BOOL fOk;
hFile = CreateFile("SOMEDATA.DAT". GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
return(FALSE);
}
pvBuf = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (pvBuf == NULL)
{
return(FALSE);
}
fOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRead, NULL);
if (ifOk || (dwNumBytesRead != 1024))
{
return(FALSE);
}
// что-то делаем с данными
...
}
__finally
{
// очистка всех ресурсов
if (pvBuf != NULL)
VirtualFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
}
// продолжаем что-то делать
return(TRUE);
}
Главное достоинство Funcarama3 в том, что весь код, отвечающий за очистку, со бран в одном месте — в блоке finally. Если понадобится включить что-то в эту функ цию, то для очистки мы просто добавим одну-единственную строку в блок finally — возвращаться к каждому месту возможного возникновения ошибки и вставлять в него строку для очистки не нужно
Windows для профессионалов
Funcarama4: последний рубеж
Настоящая проблема в Fипсаrата3 — расплата за изящество. Я уже говорил: избегай те по возможности операторов return внутри блока try.
Чтобы облегчить последнюю задачу, Microsoft ввела еще одно ключевое слово в свой компилятор С++- _leave. Вот новая версия (Funcarama4), построенная на при менении нового ключевого слова:
BOOL Funcarama4() {
// Внимание, инициализируйте все переменные, предполагая худшее
HANDLE hFile = INVALID_HANDLE_VALUE;
PVOID pvBuf = NULL;
// предполагаем, что выполнение функции будет неудачным BOOL fFunctionOk = FALSE;
__try {
DWORD dwNumBytesRead;
BOOL fOk;
hFile = CreateFile("SOMEDATA.DAT", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID,HANDLE_VALUE)
{
__leave;
}
pvBuf = VirtualAlloc(NULL, 1024, MEM_COHMIT, PAGE_READWRITE);
if (pvBuf == NULL)
{
__leave;
}
fOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRead, NULL);
if (!fOk || (dwNumBytesRead == 0))
{
__leave;
}
// что-то делаем с данными
// функция выполнена успешно fFunctionOk = TRUE; }
__finally
{
// очистка всех ресурсов
if (pvBuf != NULL)
VirtualFree(pvBuf, MEM_RELEASE | MEM__DECOMMIT);
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
} // продолжаем что-то делать
return(fFunctionOk);
}
Ключевое слово _leave в блоке try вызывает переход в конец этого блока. Може те рассматривать это как переход на закрывающую фигурную скобку блока try. И никаких неприятностей это не сулит, потому что выход из блока try и вход в блок finally происходит естественным образом. Правда, нужно ввести дополнительную бу леву переменную fFunctionOk, сообщающую о завершении функции: удачно оно или нет. Но это дает минимальные издержки.
Разрабатывая функции, использующие обработчики завершения именно так, ини циализируйте все описатели ресурсов недопустимыми значениями перед входом в блок try. Тогда в блоке finally Вы проверите, какие ресурсы выделены успешно, и узна ете тем самым, какие из них следует потом освободить. Другой распространенный
метод отслеживания ресурсов, подлежащих освобождению, — установка флага при успешном выделении ресурса. Код finally проверяет состояние флага и таким обра зом определяет, надо ли освобождать ресурс,