|
Программирование >> Перегруженные имена функций и идентификаторы
Тонкости и хитрости в вопросах и ответах Я наследовал из абстрактного класса A класс B и определил все pure-методы. А она при выполнении ругается, что в конструкторе A по прежнему зовётся абстрактный метод? Почему и что делать? Так и должно быть - в C++ конструкторы предков вызываются только до конструктора потомка, а вызов методов не инициализированного потомка может окончиться катастрофически (это верно и для деструкторов). Поэтому и была ограничена виртуальность при прямом или косвенном обращении в конструкторе (деструкторе) предка к виртуальным методам таким образом, что всегда будут вызваны методы предка, даже если они переопределены в потомке. Замечание: это достижимо подменой VMT. Практически принятое ограничение поначалу сбивает с толку, а проблемы с TV (созданным в Турбо Паскале, где конструктор сам зовёт нужные методы и конструкторы предков) доказывают незавершённость схемы конструкторов C++, в котором из-за автоматического вызова конструкторов подобъектов (предков) сложно описать конструирование объекта как целого в одном месте, поэтому конструкторы C++ правильнее называть инициализаторами. Таким образом, логичнее было бы иметь два шага: автоматический вызов инициализаторов предков (например, с обнулением указателей) и последующий автоматический же вызов общего конструктора. И в C++ это реализуемо! Для этого во всех классах нужно ввести инициализаторы (защищённые конструктор по умолчанию или конструкторы с фиктивным параметром) и в конструкторах потомка явно задавать именно их (чтобы подавить вызов конструкторов вместо инициализаторов предков). Если же код конструкторов выносить в отдельные (виртуальные) методы, то можно будет вызывать их в конструкторах потомков. С деструкторами сложнее, поскольку в классе их не может быть более одного, поэтому можно ввести отдельные методы (типа shutdown и destroy в TV). Теперь остаётся либо убрать деструкторы (хотя придётся явно вызывать методы деструкции), либо ввести общий признак, запрещающий деструкторам предков разрушать, и при переопределении метода деструкции переопределять также деструктор. И не забывайте делать их виртуальными! В качестве примера можно привести преобразование следующего фрагмента, содержащего типичную ошибку, в правильный: class PrintFile public: PrintFile(char name[]) Печaть(GetFileName(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 Печaть(GetFileName(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 может привести к баге без этого? Иногда бывает необходимо проверить механизм генерации кода, скорость работы с какой-нибудь переменной или просто
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |