Wiki

Сравнение MFC и Qt.

Автор : Pascal Audoux Перевод : Andi Peredri

После размещения этой статьи в интернете, она получила следующую критику:

  • Она написана не достаточно хорошо
  • Плохо описаны проблемы MFC
  • Нет примеров кода
  • Статья не объективна, так как Qt постоянно хвалят
  • Автор не проявляет глубоких знаний MFC и представляет все в ложном свете
  • Автор не сравнивает Qt с .NET
Хочу ответить, что написать хорошую статью не просто. Для этого потребовалось бы очень много времени, проверенных фактов, примеров кода и сравнений. Если бы у меня была такая хорошая статья, то я, вероятно, опубликовал бы ее на более профессиональных сайтах, а не на своей домашней странице.

В какой-то степени я признаю эту критику. Изначально эта статья задумывалась как небольшой обзор по программированию с помощью MFC. А в результате в ней отражено больше проблем, с которыми мы столкнулись при программировании с MFC, чем простого сравнения Qt и MFC. Но критика принимается, и моя следующая статья будет лучше.

И все же, я думаю, что представленный ниже материал по-прежнему ценен. Я не встречал ни одного сравнения MFC и Qt. Поэтому это единственное.

Philippe Fremy

Введение Я программирую используя Qt и MFC. Поэтому мне хотелось бы поделиться с Вами своим опытом использования этих двух инструментариев.

Я не пишу профессионально. Поэтому эта статья не так хороша и приятна как те, которые Вы найдете на профессиональных веб-сайтах или в журналах. Это всего лишь мой опыт, который я передаю Вам своими словами. Английский - не родной мне язык, поэтому мои конструкции, вероятно, немного странны и в них есть ошибки. Пожалуйста, сообщите мне о них, чтобы я мог их исправить.

Эта статья не претендует на объективность. Она - лишь результат моего личного опыта. Я не перечисляю в ней все хорошие и плохие особенности Qt и MFC. Тот факт, что мое знакомство с Qt произошло раньше, чем я стал программировать с MFC, мог также повлиять на мою объективность.

Эта статья написана с прагматической точки зрения: мой босс предоставляет мне спецификацию приложений, которые он хочет, а я разрабатываю их. Для разработки одних я использовал Qt, для других - MFC.

Microsoft Foundation Class (MFC) - это графический инструментарий для операционных систем Windows. Это более или менее объектно-ориентированная обертка (wrapper) для интерфейса прикладного программирования (API) win32. Предоставляемый инструментарием MFC API имеет ужасный смешанный C/C++-интерфейс.

Qt - это графический C++ -инструментарий, который разрабатывается с 1994 года Trolltech (http://www.kde.ru/"http://www.trolltech.com"). Он доступен для Windows (любой версии), Unix (любой версии), MacOS X и встроенных устройств, таких как Sharp Zaurus. Несомненно, инструментарий Qt полностью объектно-ориентирован.

Модель Document/View

Инструментарий MFC требует от программиста использования модели Document/View и шаблонов. Почти невозможно не использовать их. Но шаблоны имеют фиксированную структуру, и поэтому очень сложно расширить их возможности. Например, невозможно разделить область и отобразить два вида двух различных документов. Вторая проблема заключается в том, что шаблон создает вид, но не имеет к нему доступа. Все должно быть сделано документом, и это иногда создает проблемы.

Qt не ограничивает Вас какой-либо моделью дизайна. Без каких-либо ограничений Вы можете использовать модель Document/View, если сочтете это нужным. Или не использовать ее.

Сравнение псевдо-объектной и хорошей объектной архитектур

Главное отличие между Qt и MFC - в их дизайне.

MFC - своего рода объектная обертка к Windows API, который написан на C. Это сложно назвать хорошим объектно-ориентированным дизайном. В различных местах Вы должны будете снабжать C-структуру 15-тью членами, из которых только один будет релевантным. Или должны будете вызывать функции с устаревшими параметрами.

Также встречаются различного рода уловки, лишенные какой-либо логичности. Например, если Вы создаете графический объект, он не создастся, пока не будет вызван метод Create(). Для диалогов Вы должны ожидать OnInitDialog(), а для видов Вы ожидаете OnInitialUpdate(), ... Также, если Вы создаете объект вручную и вызываете его методы, возникают сбои.

Давайте для примера возьмем диалог, содержащий элемент управления CEdit. Вы не можете для него использовать метод GetWindowText() вне метода DoModal(). Это приведет к сбою. Почему текст элемента управления можно получить только тогда, когда он находится в определенном состоянии? Подобных уловок в MFC полно. К тому же, это усложняет отладку.

Qt - полная противоположность. Ее объектно-ориентированная архитектура, очевидно, была хорошо продумана. Поэтому инструментарий имеет согласованное именование, наследование, организацию классов и методов. Количество аргументов методов ограниченно только необходимыми. Их порядок постоянен для различных классов. А возвращаемое значение логично. И в тоже время сохраняется функциональность и простота. Использовав однажды один из классов, Вы сможете использовать и другие, потому что они работают сходным образом.

Чтобы в Qt получить элемент управления Edit, Вы должны создать объект QLineEdit с помощью оператора new, как в обычных классах C++. Тотчас же Вы получаете доступ ко всем методам объекта, независимо от того, отображен он или нет. Это работает самым простым образом, как только Вы можете себе представить.

Цикл сообщений

Работа MFC основана на механизме сообщений. Чтобы что-то сделать, Вы должны обработать определенное сообщение. Windows посылает программе тысячи сообщений. К сожалению, непросто узнать, как их использовать, какую информацию они передают, когда они посылаются. Некоторые из них излишни, некоторые не посылаются, некоторые не документированы. В документации эта тема не очень хорошо описана. Непросто узнать, какой объект что и когда посылает, какой объект получает или не получает, и какое действие было выполнено. Некоторые возможности, доступные через сообщения, предпочтительнее было бы реализовать через прямые вызовы. Механизм сообщений усложняет отладку и чтение листингов.

Работа Qt базируется на механизме обратного вызова, основанном на передаче сигналов и их приеме слотами (Slots). Эта система является основным механизмом связи между объектами. Сигнал может передавать любое число аргументов. Это очень удобно. Необходимые сигналы Вы подключаете к соответствующим слотам, так что в итоге Вы всегда знаете, что происходит. Число сигналов, передаваемых классом, обычно невелико ( 4 - 5 ), и все они очень хорошо документированы. Этот процесс находится под полным Вашим контролем. Механизм использования сигналов и слотов приближенно напоминает Java listener, но он более легковесен и универсален.

Создание интерфейса

MFC не обеспечивает компоновку (layout) элементов управления внутри окна: это создает проблемы при желании сделать окно изменяемого размера. Вы должны вручную перемещать элементы управления при изменении размеров окна (это объясняет, почему большинство диалоговых окон Windows неизменяемого размера). Эта проблема приобретает большее значение для программ, интерфейс которых должен быть переведен на язык с более длинными словами или предложениями. Вы должны будете перекомпоновать Ваши окна для каждого такого языка.

Редактор ресурсов Visual Studio весьма ограничен: Вы можете расположить элементы управления в фиксированных позициях и только. Некоторые свойства могут быть откорректированы, а мастер класса позволит Вам легко создать переменные и методы. Однако стоит заметить, что создать вручную цикл сообщений, функцию DDX ( Dialog Data Exchange ) или макрос IMPLEMENT_DYNCREATE будет достаточно сложно.

С Qt все можно сделать вручную, так как это несложно. Чтобы получить кнопку, Вам достаточно написать:

button = new QPushButton( "button text", MyParentWidget);

Если Вы хотите, чтобы при нажатии на кнопку был выполнен метод action(),Вам следует написать следующее:

connect( button, SIGNAL( clicked() ), SLOT( action() ) );

Qt имеет гибкий механизм компоновки, который является настолько простым, что Вы тратите впустую время, если его не используете.

Для создания пользовательского графического интерфейса Qt предлагает инструмент Qt Designer. С помощью него Вы можете откорректировать любые свойства используемых элементов управления. Вы не фиксируете жестко их расположение, а используете компоновщик (layout manager). Также Вы можете подключить сигналы к слотам. Это делает Qt Designer большим, нежели обычный дизайнер интерфейса. Он генерирует код, который Вы фактически можете читать и понимать. Поскольку сгенерированный код сохраняется в отдельном файле, Вы можете изменить его в любой момент.

Qt Designer позволяет Вам делать то, что невозможно в MFC, например, создать список с предзаполненными полями или использовать различного вида вкладки (tab controls).

Документация

Если Вы желаете использовать богатый графический инструментарий, то его документация для Вас будет иметь большое значение. Документация MSDN (которую Вы должны будете оплатить отдельно) огромна - на 10 CD-ROM. Она охватывает множество разнообразных тематик. Однако возникает ощущение, что она плохо отобрана и этим представляет меньшую ценность. Перекрестные ссылки также очень бедны. Трудно переходить к базовым, родственным классам и классам-потомкам. Методы представлены без сигнатуры. Трудно обратиться к унаследованным методам класса. Другая проблема состоит в том, что если Вы производите поиск по ключевому слову, то получаете результат для VC++, Visual Basic, Visual J++ и InterDev, даже если используете фильтр.

Документация Qt просто превосходна. Лучше взгляните сами: doc.trolltech.com

Она охватывает все области применения Qt и занимает 18 Мбайт. Каждый класс и метод подробно документирован с множеством примеров использования. Удобная навигация между классами и методами осуществима любым веб-браузером или инструментом, предлагаемым Trolltech (Qt Assistant). Вместе с документацией поставляются руководство и примеры использования. Также имеется FAQ и список рассылки, доступный через группы новостей и веб-интерфейс с возможностью поиска. Если у Вас есть лицензия, Вы поможете воспользоваться технической поддержкой, обычно реагирующей в течение одного дня.

Хорошая продуманность Qt уменьшает потребность в посторонней помощи. Одно из заявлений Trolltech гласит: "Продукт и документация должны быть настолько хороши, чтобы не возникало потребности в технической поддержке".

Уникод

Чтобы отобразить уникод в MFC, Вы должны скомпилировать и собрать приложение со специальными опциями (а также сменить точку входа исполняемого модуля). Вы должны добавить _T к каждой строке, используемой в программе, и сменить тип `char` на TCHAR. Все функции, обрабатывающие строки (strcpy, strdup, strcat, ...), должны быть заменены другими. Самое досадное то, что программа, откомпилированная с поддержкой уникода, не будет работать с библиотеками DLL, откомпилированными без его поддержки. Если при разработке Вы используете сторонние библиотеки DLL, это может доставить Вам серьезные неудобства.

Строки в Qt являются объектами класса QString, который изначально поддерживает уникод. Поэтому Вам не придется изменять свой код или использовать какие-либо опции при компиляции и сборке. Просто используйте QString. Qt изначально поддерживает уникод, поэтому здесь не будет никаких проблем.

Сам по себе класс QString очень функционален, даже если Вы и не заботитесь об уникоде. Поэтому поддержка уникода достигается весьма просто. В случае необходимости QString может обеспечить перевод строки в char* или UTF8.

Большое различие между строковым классом MFC CString и QString заключается в их дизайне. CString основан на типе char* с несколькими методами. Преимущество такого подхода в том, что везде, где требуется переменная типа char*, Вы можете использовать член класса CString. Однако это привлекательно только на первый взгляд. Большим недостатком такого подхода является то, что Вы можете изменить член char* класса CString без обновления самого объекта класса. Также возникают трудности при преобразовании в уникод.

QString, напротив, содержит уникод-версию строки и обеспечивает тип char* только когда требуется. API Qt в качестве строчных аргументов всегда требует QString, поэтому Вам редко придется использовать тип char*. Класс QString также обладает некоторыми дополнительными возможностями, вроде совместного использования (или копирования по требованию) содержимого строки. Поэтому Вам наверняка захочется его использовать в своих программах. Это типичный пример хорошей архитектуры Qt и С++ -оберточной MFC.

Локализация

MFC-программу можно локализировать. Для этого Вы должны поместить каждую строку в строковую таблицу и повсюду в Вашем коде использовать функцию LoadString( IDENTIFIER ). Затем Вы должны поместить строковую таблицу в библиотеку DLL. Используя Visual Studio, перевести строки в желаемый язык, преобразовать графические ресурсы (потому что их текст не может быть помещен в строковую таблицу) и использовать эту библиотеку в программе. Это настолько сложно, что Вы, вероятно, не сможете доверить эту работу одному переводчику, ему нужна будет Ваша помощь. У Вас также могут возникнуть проблемы из-за фиксированного позиционирования элементов управления в MFC. Так как их расположение определялось длиной непереведенных строк, более длинные переведенные фразы будут накладываться. Если в последствии Вы поменяете некоторые строки или добавите новые, Вы должны будете убедиться, что перевод был обновлен.

В Qt Вы всего лишь передаете ваши строки функции tr() . Это очень удобно при разработке - Вы можете изменять строки непосредственно в Вашем коде. Специальная программа Qt Linguist извлечет все требующие перевода строки и в удобном виде их отобразит. Ее интерфейс обеспечивает удобный перевод, возможность использования словаря, просмотр контекста строки, обнаружение конфликта клавиатурных сокращений, обнаружение новых непереведенных или измененных строк. Эта программа может использоваться переводчиком без познаний в области разработки программ. Редактор Qt Linguist доступен по лицензии GPL, поэтому Вы можете его модифицировать. Перевод сохраняется в формате XML, поэтому он может быть легко использован в различных целях. Задача добавления к программе нового перевода заключается в создании нового файла с помощью Qt Linguist.

Проблемы ресурсов приложений

Процесс разработки с помощью MFC тесно связан с созданием ресурсов приложений. Они используются во многих случаях. Это имеет следующие последствия:

  • Практически невозможно заниматься разработкой программ с помощью средств, отличных от Visual Studio.
  • Редактор ресурсов весьма ограничен в своих возможностях. Например, сделать абсолютно все с помощью редактора диалогов невозможно: некоторые свойства могут быть изменены, а некоторые нет. Так, для панелей инструментов Вы вынуждены использовать изображение, которое содержит изображения всех кнопок. Чтобы приложению дать название, Вы должны поместить некоторые строки в строковую таблицу, но Вы должны знать, что каждое поле отделяется символом ` `. Порядок и значение каждого поля не очевидны, и их нелегко определить.
  • В файле "Resource.h" идентификаторам ресурсов присваиваются значения (числа, меньшие 32768). Это создает проблемы при удалении или переименовании ресурсов, а также в случае, когда различные библиотеки DLL используют файлы "resource.h" с одинаковыми названиями ресурсов, но с различными их значениями (типичный случай при использовании DLL-компонент).
  • При использовании библиотеки DLL с ее собственными ресурсами, которая, в свою очередь, использует другие DLL-библиотеки, возникает вероятность, что программа может смешать свои ресурсы с ресурсами подключаемых библиотек, так как ресурсы могут иметь одинаковые значения идентификаторов. Поэтому Вы должны зарезервировать для своих ресурсов диапазон уникальных значений. Но это не всегда возможно, так как библиотеки могут быть для Вас неподконтрольны.

Отсутствие концепции ресурсов в Qt решает все вышеперечисленные проблемы. Qt предлагает небольшой сценарий, позволяющий включать изображения в Ваш код. А для создаваемого интерфейса Qt Designer генерит читаемый код.

Цены

MFC SDK Вы получаете бесплатно при покупке Visual Studio.

UNIX-версия Qt свободна (доступна по лицензии GPL) для разработки свободного программного обеспечения. Также доступна некоммерческая версия для платформы Windows. Но для разработки коммерческого программного обеспечения Вы должны приобрести лицензию Qt. Лицензируется в отдельности каждая платформа (Unix, MacOS или Windows) и каждый разработчик. Лицензия приобретается раз и навсегда и включает в себя один год технической поддержки. Выплаты за распространение runtime-библиотеки отсутствуют. Цена составляет 1550 $, что очень дорого для небольшой компании. При покупке более одной лицензии предлагаются скидки. Заметьте, что цена меньше затрат двухнедельного содержания разработчика. Инвестиции стоят того.

Распространение

При распространении приложения, созданного с помощью MFC, Вы можете положиться на MFC-библиотеку, поставляемую вместе с Windows. Но это небезопасно. Под одним и тем же названием, MFC42.DLL, могут скрываться три различные версии этой библиотеки. Поэтому нужно убедиться, что пользователь имеет необходимую версию библиотеки, и, в противном случае, ее обновить. Обновление библиотеки MFC может повлиять на работу многих приложений. Это недостаточно удобно. Что, если после установки моей программы компьютер перестанет работать?

Названия библиотек Qt недвусмысленны ( qt-mt303.dll ), поэтому нет никакого риска, что установка библиотеки qt-mt303.dll повлияет на работу приложения, использующего, скажем, qt-203.dll. Также отсутствует проблема обновления системы в целом.

Другие преимущества Qt

Инструментарий Qt обладает не только функциональностью MFC, доступной более простым путем, но и многими возможностями, не имеющими аналогов в MFC:

  • Элементы управления: Qt - это графический инструментарий, предлагающий богатый набор графических элементов управления: текстовые поля, раскрывающиеся списки, кнопки-переключатели, ... Некоторые из них очень сложны. Например, список (listview) обладает возможностью многоколоночного представления с пиктограммами и кнопками-переключателями.
  • XML: Qt предлагает классы для работы с документами XML ( используется Sax2 или Dom ). Это простая, надежная и функциональная реализация.
  • Регулярные выражения: Qt предлагает полную поддержку для Perl-совместимых регулярных выражений. Это дальнейшее развитие простых метасимволов `?` и `*`. Регулярные выражения - это очень мощный инструмент для анализа данных, поиска образцов внутри документа, создания фильтров, определения маски для полей ввода.
  • Много-платформенность: Qt 3 доступна для Windows (любой версии), Unix (любой версии), MacOS X и встроенных устройств. Для запуска Вашего приложения на другой платформе Вам достаточно его лишь перекомпилировать. Если компилятор не имеет каких-либо особенностей или ограничений, Вам не нужно прикасаться к Вашему коду.
  • Классы шаблонов: Qt предлагает полезные классы для манипулирования списками, файлами, каталогами, строками, потоками, ... Они более функциональны и удобны, чем аналогичные из STL и MFC.
  • Управление памятью: Qt обладает различными средствами, упрощающими управление памятью. Объекты Qt автоматически уничтожаются при уничтожении их родителя. Многие классы обладают возможностью неявного совместного использования, которая освобождает Вас от забот по уничтожению и копированию этих объектов.
  • Сетевой API: Qt предоставляет классы, облегчающие написание сетевых приложений: сокеты, DNS, FTP, HTTP, ...
  • API баз данных: Qt предоставляет классы для работы с базами данных: специальные виджеты, подключения к базам данных, SQL-запросы, ...
  • OpenGL API: Qt предлагает классы для работы с библиотекой OpenGL.
  • Двухмерная графика ( Canvas ) : Qt предлагает классы, оптимизированные для обработки быстро перемещающихся двухмерных объектов, известных как спрайты.
  • Стили: Можно полностью настроить внешний вид элементов управления. Qt может эмулировать стили всех известных инструментариев: Motiff, MFC, NextStep, ...

Что такое CodeJock ?

Многочисленные недостатки MFC явились причиной возникновения различных MFC-оберток, которые помогают легко создавать приложения. Мы использовали библиотеку CodeJock. Как CodeJock + MFC сравнится с Qt ?

  • CodeJock - это обертка для библиотеки MFC, которая, в свою очередь, является оберткой для API Windows. Добавление большего количества оберток для скрытия проблем обычно не является хорошим решением. Все вышеперечисленные проблемы все еще существуют ( ресурсы, шаблоны, сообщения, уникод, интернационализация, ... )
  • Классы, предлагаемые библиотекой CodeJock, позволяют более легко использовать MFC ( за счет дополнительных возможностей, упрощенных методов или их большего количества). Например, с ее помощью можно создать элемент управления Multi-Tab View, в то время как с MFC это принципиально невозможно. Однако библиотека весьма ограничена. Она предлагает чуть больше классов, чем MFC, а не полный набор. CodeJock больше напоминает набор исправлений, а не обертку.
  • Качество библиотеки оставляет желать лучшего. Много ошибок не исправлено, добавлены новые. На протяжении первых 6 месяцев 2002 года было выпущено 3 версии ( 1.9.2.1, 1.9.2.2, 1.9.3.0 ), в каждой из которых исправлено более 50 ошибок. Библиотека не отлажена и не протестирована. Это не инструмент профессионального качества. Ее пользователи - это альфа-тестеры. Заметьте также, что API библиотеки меняется от версии к версии, поэтому Вы должны будете изменять свой код. Это признаки плохого дизайна.
  • Чтение кода ( к сожалению, неизбежное для CodeJock, учитывая ее временами странное поведение ) раскрывает ужасную картину: методы более чем на 500 строк, избыточный код, множество переходов в середину из ниоткуда, отсутствие комментариев, различные уловки. Многочисленные методы классов объявлены общедоступными, тогда как должны быть защищенными и т.д.
  • Документация бедна, а в некоторых местах и вовсе пуста ( методы присутствуют без каких-либо объяснений ). Похоже, что на протяжении последних выпусков она не обновлялась.
  • CodeJock не обладает возможностями, которые нельзя было бы найти в Qt. Исключением является шестнадцатеричный редактор, который к сожалению имеет множество ошибок, и который Вы легко можете сделать в Qt ( примеры уже существуют ).
У Qt отличное качество кода. Библиотека надежна и устойчива. С целью ее значительного улучшения на протяжении последних 6 лет исходная и двоичная совместимости были лишь дважды нарушены. И только один раз ( при переходе от Qt1 к Qt2 ) требовалась существенная переделка кода.

Вывод

Вывод, следующий из полученного нами опыта, очевиден. Qt намного лучше MFC. Вы создаете программы лучше и быстрее.

Некоторые люди жаловались, что эта статья снисходительна к Qt и не раскрывает всех возможностей MFC. Просто это наш опыт: мы имели множество проблем с MFC, и почти не имели их с Qt. Разработка с помощью Qt всегда была простой, документированной и эффективной. Если MFC и имеет преимущества, то мы их не нашли, за исключением бесплатной поставки вместе с Visual Studio.

Мы доступны для обратной связи: пишите нам для предложений, усовершенствований, замечаний и флейма !

Я хотел бы включить замечания людей, использовавших MFC и Qt. Если Вы к ним относитесь, напишите мне, пожалуйста.