Программирование >>  Арифметические и логические операции 

1 ... 33 34 35 [ 36 ] 37 38 39 ... 53


Функция WinMain

Последний этап - написание функции WinMain в которой будет создаваться главное окно, устанавливаться значок в системную область панели задач, ставиться и сниматься ловушки. Код её должен быть примерно такой:

WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow)

MSG msg;

----------------

hLib=LoadLibrary( SSHook.dll );

if(hLib)

(void*)pKeybHook=GetProcAddress(hLib, KeyboardHook ); hKeybHook=SetWindowsHookEx(WH KEYBOARD,(HOOKPROC)(pKeybHook), hLib,0); Ставим ловушки

(void*)pMouseHook=GetProcAddress(hLib, MouseHook ); hMouseHook=SetWindowsHookEx(WH MOUSE,(HOOKPROC)(pMouseHook),

hLib,0);

-------------------------------if (InitApplication(hInstance,nCmdShow)) Если создали главное окно

vfSetTrayIcon(hInstance); Установили значок while (GetMessage(&msg,(HWND)(NULL),0,0)) { Цикл обработки сообщений TranslateMessage(&msg); DispatchMessage(&msg);

--------- Всё - финал

UnhookWindowsHookEx(hKeybHook); Снимаем ловушки UnhookWindowsHookEx(hMouseHook); FreeLibrary(hLib); Отключаем DLL vfResetTrayIcon(); Удаляем значок return 0;

return 1;

После написания этой функции можно смело запускать полностью готовое приложение.

Вопросы и ответы

Я наследовал из абстрактного класса A класс B и определил все pure-методы. А она при выполнении ругается, что в конструкторе A по прежнему зовётся абстрактный метод? Почему и что делать?

Так и должно быть - в C++ конструкторы предков вызываются только до конструктора потомка, а вызов методов не инициализированного потомка может окончиться катастрофически (это верно и для деструкторов).

Поэтому и была ограничена виртуальность при прямом или косвенном обращении в конструкторе (деструкторе) предка к виртуальным методам таким образом, что всегда будут вызваны методы предка, даже если они переопределены в потомке.

Замечание: это достижимо подменой VMT.

Практически принятое ограничение поначалу сбивает с толку, а проблемы с TV (созданным в Турбо Паскале, где конструктор сам зовёт нужные методы и конструкторы предков) доказывают незавершённость схемы конструкторов C++, в котором из-за автоматического вызова конструкторов подобъектов (предков) сложно описать конструирование объекта как целого в одном месте, поэтому конструкторы C++ правильнее называть инициализаторами.

Таким образом, логичнее было бы иметь два шага: автоматический вызов инициализаторов предков (например, с обнулением указателей) и последующий автоматический же вызов общего конструктора. И в C++ это реализуемо!

Для этого во всех классах нужно ввести инициализаторы (защи-щённые конструктор по умолчанию или конструкторы с фиктивным параметром) и в конструкторах потомка явно задавать именно их (чтобы подавить вызов конструкторов вместо инициализаторов предков). Если же код конструкторов выносить в отдельные (виртуальные) методы, то можно будет вызывать их в конструкторах потомков.

С деструкторами сложнее, поскольку в классе их не может быть более одного, поэтому можно ввести отдельные методы (типа shutdown и destroy в TV).

Теперь остаётся либо убрать деструкторы (хотя придётся явно вызывать методы деструкции), либо ввести общий признак, запрещающий



деструкторам предков разрушать, и при переопределении метода деструкции переопределять также деструктор. И не забывайте делать их виртуальными!

В качестве примера можно привести преобразование следующего фрагмента, содержащего типичную ошибку, в правильный:

class PrintFile public:

PrintFile(char name[]) Печать(GetFi1eName(name, MyExt())); virtual const char *MyExt() return xxx ;

class PrintAnotherTypeOfFile :public PrintFile public:

PrintAnotherTypeOfFile(char name[]) :PrintFile(name) const char *MyExt() return yyy ;

После преобразования получаем следующее:

class PrintFile

enum Init Init; Тип фиктивного параметра protected: Инициализатор; здесь можно заменить на дефолт конструктор PrintFile(Init ).

Можно добавить несколько конструкторов с другими именами, или, если конструкторы не виртуальные, можно использовать полиморфизм:

bool construct(char name[])

return Печать(GetFi1eName(name,MyExt()));

public:

... Код вынесен в отдельный метод для использования в потомках PrintFile(char name[]) construct(name); virtual const char *MyExt() return xxx ;

class PrintAnotherTypeOfFile :public PrintFile

... Здесь инициализатор пропущен (никто не наследует)

public:

... Конструктор; использует конструктор предка, с виртуальностью;

... указание инициализатора обязательно PrintAnotherTypeOfFile(char name[]) :PrintFile(Init) construct(name);

const char *MyExt() return yyy ;

Что такое NAN?

Специальное значение вещественного числа, обозначающее нечисло - Non-a-Number. Имеет характеристику (смещенный порядок) из всех единиц, любой знак и любую мантиссу за исключением .00 00 (такая мантисса обозначает бесконечность). Имеются даже два типа не чисел:

♦ SNAN - Signalling NAN (сигнализирующие не-числа) - старший бит мантиссы=0

♦ QNAN - Quiet NAN (тихие не-числа) - старший бит мантиссы = 1.

SNAN никогда не формируется FPU как результат операции, но может служить аргументом команд, вызывая при этом случай недействительной операции.

QNAN=11 11.100 00 (называется еще вещественной неопределенностью ), формируется FPU при выполнении недействительной операции, делении 0 на 0, умножении 0 на бесконечность, извлечении корня FSQRT, вычислении логарифма FYL2X отрицательного числа, и т.д. при условии, что обработчик таких особых случаев замаскирован (регистр CW, бит IM=1). В противном случае вызывается обработчик прерывания (Int 10h) и операнды остаются неизменными.

Остальные не-числа могут определяться и использоваться программистом для облегчения отладки (например, обработчик может сохранить для последующего анализа состояние задачи в момент возникновения особого случая).

Как выключить оптимизацию и как longjmp может привести к баге без этого?

Иногда бывает необходимо проверить механизм генерации кода, скорость работы с какой-нибудь переменной или просто использовать переменную в параллельных процедурах (например, обработчиках прерываний). Чтобы компилятор не изничтожал такую переменную и не делал её регистровой придумали ключевое слово volatile.

longjmp получает переменная типа jmp buf, в которой setjmp сохраняет текущий контекст (все регистры), кроме значения переменных. То есть если между setjmp и longjmp переменная изменится, её значение восстановлено не будет.

Содержимое переменной типа jmp buf никто никогда (кроме setjmp) не модифицирует - компилятор просто не знает про это, потому что все это не языковое средство.



Поэтому при longjmp в отличие от прочих регистровые переменные вернутся в исходное состояние (и избежать этого нельзя). Также компилятор обычно не знает, что вызов функции может привести к передаче управления в пределах данной функции. Поэтому в некоторых случаях он может не изменить значение переменной (например, полагая ее выходящей из области использования).

Модификатор volatile в данном случае поможет только тем переменным, к к которым он применён, поскольку он никак не влияет на оптимизацию работы с другими переменными...

Как получить адрес члена класса?

Поскольку указатель на член, в отличие от простого указателя, должен хранить также и контекстную информацию, поэтому его тип отличается от прочих указателей и не может быть приведён к void*. Выглядит же он так:

int i; int f();

struct X int i; int f(); x, *px = &x;

int *pi = &i; i = *pi;

int (*pf)() = &f; i = (*pf)();

int X::*pxi = &X::i; i = x.*pxi;

int (X::*pxf)() = &X::f; i = (px->*pxf)();

Зачем нужен for, если он практически идентичен while?

Уточним различие циклов for и while:

♦ for позволяет иметь локальные переменные с инициализацией;

♦ continue не обходит стороной выражение шага, поэтому

for(int i = 0; i < 10; ... continue; ...

не идентично

int i = 0; while(i < 10)

continue;

Зачем нужен NULL?

Формально стандарты утверждают, что NULL идентичен 0 и для обоих гарантируется корректное преобразование к типу указателя.

Ho разница есть для случая функций с переменным числом аргументов (например, printf) - не зная типа параметров компилятор не может преобразовать 0 к типу указателя (а на писюках NULL может быть равным 0L).

С другой стороны, в нынешней редакции стандарта NULL не спасёт в случае полиморфности: когда параметр в одной функции int, а в другой указатель, при вызове и с 0, и с NULL будет вызвана первая.

Безопасно ли delete NULL? Можно ли применять delete()var после new varO? А что будет при delete data; delete data?

♦ delete NULL (как и free(NULL)) по стандарту безопасны;

♦ delete[] после new, как и delete после new[] по стандарту применять нельзя.

Если какие-то реализации допускают это - это их проблемы;

♦ повторное применение delete к освобождённому (или просто не выделенному участку) обладает неопределённым поведением и может вызвать всё, что угодно - core dump, сообщение об ошибке, форматирование диска и прочее;

♦ последняя проблема может проявиться следующим образом:

new data1; delete data1; new data2;

delete data1; delete data2;

Что за чехарда с конструкторами? Деструкторы явно вызываются чаще...

На это существует неписанное Правило Большой четвёрки : если вы сами не озаботитесь о дефолтном конструкторе, конструкторе копирования, операторе присваивания и виртуальном деструкторе, то либо Старший Брат озаботит вас этим по умолчанию (первые три), либо через указатель будет дестроиться некорректно (четвёртый).

Например:

struct String1 ... char *ptr; String1 &operator = (String1&); struct String2 ... char array[lala];

В результате отсутствия оператора присваивания в String2 происходило лишнее копирование String2::array в дефолтном операторе присваивания, поскольку String1::operator = и так уже дублировал строку ptr. Пришлось вставить.

Так же часто забывают про конструктор копирования, который вызывается для передачи по значению и для временных объектов. А ещё есть чехарда с тем, что считать конструктором копирования или операторами присваивания:

struct C0 C0 &operator = (C0 &src) puts( C0= ); return *this; ;

struct C1 :C0 C0 & operator = (C0 &src) puts( C1= ); return



1 ... 33 34 35 [ 36 ] 37 38 39 ... 53

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика