понедельник, 12 марта 2012 г.

Перенесу сюда сообщение из своего аккаунта в контакте:
Выяснение отношений с непокорным C++ привело меня к заключению, что чтобы детально разбираться во всех тонкостях инициализации и уничтожения объектов классов нужно положить на это жизнь. Если при попытке писать что-то ООП-шное вы часто получаете непонятные ошибки, возможно, причина кроется как раз в непонимании одного из 847528345-и моментов, касающихся именно этой области C++. Очень хорошо эти вопросы освещены в соответсвующих главах Брюса Эккеля "Философия C++, том 1", в которых будет проще и продуктивнее следить за мыслью автора с помощью вот этого: https://github.com/ramntry/constructors
Получить пакет и попробовать запустить пример на *nix-совместимых системах можно следующим образом:

git clone git://github.com/ramntry/constructors.git
cd constructors
./constructors -f examples/virtual_destructors.cpp
В системе должны быть установлены bash (или другой совместимый shell), git, g++ (gcc) и sed

понедельник, 5 марта 2012 г.

Qt и шаблоны

Не пытайтесь создать шаблонный класс, использующий мета-возможности Qt - в текующей реализации (Qt 4.8) ключевое слово template перед заголовком класса и макрос Q_OBJECT в его приватном блоке вещи несовместимые (просто шаблонный наследник QObject имет право на существование, но ведь это уже совсем другая история)

Поведение системы сборки Qt

QГрабля#4

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

Решение

Всегда определяйте Qt-классы в отдельных заголовочных файлах. Реализация такого класса допустима как in situ, так в *.cpp-файле.

UPD


Также об этой проблеме можно почитать в небольшой ветке форума Qt

Управление памятью в Qt

QГрабля#3


Известно, что немалая часть объектов Qt, с которыми приходится иметь дело, в той или иной мере являются контейнерами для других объектов, иногда это непосредственно так (например, для layout'ов), иногда вырождается в повсеместно существующую возможность назначить объекту некоторого родителя. Так или иначе, крайне часто вы передаете в метод (в т. ч. конструктор) объекта указатель на другой объект. Не таким уж странным в таком случае кажется решение создать объект на стеке, а в случае необходимости его куда-то передать просто использовать оператор взятия адреса.

Однако Qt в рамках своей юрисдикции сама управляет памятью - если вы удаляете объект, его деструктор автоматически удаляет всех своих детей и (вероятно) элементы, содержащиеся в нем. Естесственно, ей невозможно знать, был ли создан указатель операцией new, или он указывает на объект, для которого вовсе не освобождалась память в куче - при удалении вашему указателю сделают delete, что выльется в реальной практике в backtrace и memory dump на экран при завершении Qt-шного приложения, если этот указатель увел delete на стек. Однако если вы все объекты будете создавать в куче не заботясь об их удалении, то вы получите утечки памяти. Ясно, что позаботиться обо всех объектах также невозможно разумным образом - в частности, не удалить экземпляр QApplication (QCoreApplication) без странных костылей типа

int returnCode = app->exec();
delete app;
return returnCode;

которых вы никогда не увидите ни в одном руководстве по Qt. Ровно те же проблемы возникнут с объектом главного окна приложения.

Решение.

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


UPD


Несомненно более точно и тонко проблему освещает таки документация Qt
QГрабля#2


Если ваш класс использует систему свойств Qt, то вы можете неожиданно получить проблему на стадии компиляции при попытке вызвать setProperty.
( <YourClass or ParentClass>::setProperty("PropertyName", propertyValue); )

Вам сообщат что-то в духе
"no known conversion for argument 2 from <propertyValueType> to ‘const QVariant&"

Проблема связана с тем, что по известным причинам свойства имеют тип QVariant, а в данном блоке кода компилятору просто не известен этот тип.

Решение

Включайте заголовочный файл <QVariant> (QtCore/QVariant> в файлы-определения классов,
использующих свойства Qt. (или даже в main-файл)

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

QГрабля#1


Если в приватной секции определения класса, использующего метаобъектную систему Qt, вы забыли указать макрос Q_OBJECT, а затем исправили свою оплошность, то вы вполне можете снова (и снова, и снова...) получить ошибку компиляции со словами в духе "undefined reference" и "vtable of <YourClass>" в строках клиентского кода, использующих в вашем классе дополнительные возможности наследников QObject (сигналы и слоты, свойства, возможности интроспекции).
Подобное может произойти в том случае, если ранее ваш класс, определенный без макроса Q_OBJECT уже успешно компилировался (то есть, дополнительные возможности не использовались) - и в папке сборки уже лежит Makefile, сгенерированный по *.pro-файлу утилитой qmake. Дело в том, что даже после внесенных исправлений, пересборка проекта заключается в вызове make, подхватывающей старый Makefile, который не вызывает MOC для файлов вашего класса (так что даже make clean не поможет).

Решение


Вручную удалите Makefile в директории сборки проекта и пересоберите его.

Так же полезно завести в директории ~/bin файлик с содержимым вроде


#!/bin/sh
if [ -e "Makefile" ]
then
    make clean
    rm Makefile
fi
qmake -project
qmake
make
(не забудьте сделать chmod +x <имя файла> и убедиться в том, что ~/bin есть в переменной окружения PATH (иначе можно ее туда добавить, дописав в ~/.profile строку
PATH="$HOME/bin:$PATH" ))

... и затем собирать проекты вызовом этого скрипта. У меня это выглядит так:

qqmake