Russian Qt Forum
Ноябрь 23, 2024, 13:52 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Несколько вопросов от новичка в QT [Решено]  (Прочитано 8407 раз)
TsysarAndrew
Гость
« : Январь 19, 2014, 11:25 »

Добрый день!

Пытаюсь освоить QT (5.2.0) под windows, появилось несколько вопросов:
1) Есть проект, в нем класс Data, пытаюсь сделать для него юнит-тесты. Создал при помощи мастера проект типа "QT Unit Test", в файле .pro прописал при помощи INCLUDEPATH путь к исходникам тестируемого проекта, в .cpp теста при помощи #include прописал хедер класса Data (хедер находится по Ctr+пробел и открывается по Ctr+клик левой кнопкой), в классе теста объявлено поле
Код:
Data *f_data;
, далее в конструкторе прописано:
Код:
f_data = new Data();

и в одном из тестовых методов
Код:
f_data->proccessFile(...);

Получаю ошибки компиляции:
а) Для кода в конструторе: undefined reference to `Data::Data()'
б) Для кода в методе теста: undefined reference to `Data::proccessFile(QString, QString)'

Если закомментировать указанный код и оставить только объявление поля f_data, то проект компилируется, а при остановке отладчика в одном из тестовых методов видно, что данное поле проинициализировано. Не могу понять в чем тут проблема...

2) Объясните как правильно инициализировать поля классов. Например, объявляю я поля в классе с типом класс. Если в конструкторе специально не прописывать инициализацию (программирую в Delphi, там это обязательно), то объект все равно создается,  но в какие-то моменты жизни программы он доступен, а другие нет. Например, есть класс с полем типа QVector, он (класс) предоставляет метод для регистрации у себя других объектов, создается глобальный экземпляр этого класса-регистратора, далее регистрируемые классы у себя в конструкторе пытаются зарегистрироваться, при этом для одних регистрация проходит нормально, для других получается, что при вызове метода регистрации поле QVector еще не инициализировано...

3) Зачем в хедерах объявления классов оборачиваются в конструкции вида:
Код:
#ifndef DATA_H
#define DATA_H

....

#endif // DATA_H
« Последнее редактирование: Январь 21, 2014, 19:47 от TsysarAndrew » Записан
Vamireh
Гость
« Ответ #1 : Январь 19, 2014, 12:31 »

1) Конструктор по умолчанию есть?
2) Не понял вопроса. Я инициализирую так:
Data::Data() :
p_data(),
p_some(),
...
{
}
3) Не знаю, как сформулировать. Короче, это нужно, если этот хедер подключается из нескольких файлов. Чтобы не было множественного определения. А так дефайнится DATA_H если он не задефайнен, а при последующих подключениях - он уже задефайнен и дальнейший код не выполняется. Хотя можно просто писать вверху #pragma once, но по теории это не всеми компиляторами поддерживается (а если не поддерживается - то не ругнется даже), но на практике я не сталкивался, чтобы это не работало.
« Последнее редактирование: Январь 19, 2014, 12:34 от Vamireh » Записан
Bepec
Гость
« Ответ #2 : Январь 19, 2014, 12:48 »

2) Это называется страж. Позволяет подключать класс только один раз. Если подключить в множестве мест, вылетят интересные ошибки и у компилятора голова заболит. Каждый страж должен быть уникальным. Многие используют GUID вместо "НАЗВАНИЕ_КЛАССА".
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Январь 19, 2014, 15:24 »

Например, есть класс с полем типа QVector, он (класс) предоставляет метод для регистрации у себя других объектов, создается глобальный экземпляр этого класса-регистратора, далее регистрируемые классы у себя в конструкторе пытаются зарегистрироваться, при этом для одних регистрация проходит нормально, для других получается, что при вызове метода регистрации поле QVector еще не инициализировано...
Неясно что подразумевается под "регистрацией". Приведите пример кода - тогда можно ответить конкретно
Записан
TsysarAndrew
Гость
« Ответ #4 : Январь 19, 2014, 18:42 »

1) Конструктор по умолчанию есть?
Не совсем понял что это такое.. Если под конструктором по умолчанию подразумевается конструктор без параметров, то да, такой конструктор есть. У прощенный код объявления класса:
Код:
class Data
{
public:
    Data();
    ~Data();
....
};

Дело в том, что в самом рабочем проекте для создания объекта этого типа используется тот же самый код:
Код:
f_data = new Data();
и там все работает..
Записан
TsysarAndrew
Гость
« Ответ #5 : Январь 19, 2014, 19:35 »

Неясно что подразумевается под "регистрацией". Приведите пример кода - тогда можно ответить конкретно

Пример: есть класс ObjectsDispatcher:
Код:
class ObjectsDispatcher
{
public:
    ObjectsDispatcher();
    void registerObject(IObject* object);
    .....
private:
   QMap<QString, IObject*> f_objects;
};

Также есть некоторое количество наследников класса IObject, у которых в конструкторе прописано
Код:
qObjectsDispatcher.registerObject(this);

qObjectsDispatcher объявлен в objectsdispatcher.h:
Код:
.....
#endif // OBJECTSDISPATCHER_H
extern ObjectsDispatcher qObjectsDispatcher;

объект создается в objectsdispatcher.cpp:
Код:
 ObjectsDispatcher qObjectsDispatcher;

Далее получается, что при создании некоторых наследников IObject все проходит хорошо, а при создании других (вроде это происходило при добавлении в проект новых наследников, для каждого такого класса аналогичным образом создавался глобальный объект, которые и регистрировал себя в диспетчере) происходит исключение SIGSEGV, при этом в отладчике поле f_objects класса ObjectsDispatcher отображается как <not accessible>. Немного позже конструктор класса сделал таким (до этого его тело было пустым):
Код:
ObjectsDispatcher::ObjectsDispatcher()
{
    f_objects.clear();
}

теперь вроде все работает, но не понятно почему так работает. Особенно не понятно когда создается f_objects, почему не нужно явно его инициализировать. Ранее описанная проблема с QVector примерно такая же (там тоже вызвал clear по аналогии и вроде все пока работает).
« Последнее редактирование: Январь 19, 2014, 19:37 от TsysarAndrew » Записан
_OLEGator_
Гость
« Ответ #6 : Январь 19, 2014, 20:04 »

объект создается в objectsdispatcher.cpp:
Код:
 ObjectsDispatcher qObjectsDispatcher;

Все, дальше можно и не читать и на этой строчке отправлять учить C++, а уже после этого переходить к Qt.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Январь 19, 2014, 20:09 »

Сначала поставьте точку останова в ObjectsDispatcher::ObjectsDispatcher и вторую в ObjectsDispatcher::registerObject. Убедитесь что конструктор срабатывает первым (хоть и редко но возможно что нет). В любом случае  f_objects.clear() - просто заплатка которая ничего не решает, вылезет в др месте. Добавьте метод печатающий ObjectsDispatcher::f_objects и натыкайте его вызовы (ищите где портится)

Основная идея: если класс имеет конструктор, то невозможно создать объект такого класса минуя вызов конструктора. Явное указание конструктора выбирает который используется, но все равно 1 (и только 1) конструктор выполнится. То же самое для всех членов класса.

Все, дальше можно и не читать и на этой строчке отправлять учить C++, а уже после этого переходить к Qt.
Не понял столь жесткой критики - ну объявил человек глобальную переменную без всяких затей/синглтонов, что в этом такого уж плохого?
Записан
TsysarAndrew
Гость
« Ответ #8 : Январь 19, 2014, 20:11 »

Все, дальше можно и не читать и на этой строчке отправлять учить C++, а уже после этого переходить к Qt.

Это все замечательно и очень полезно, но в имеющихся у меня книжках (включая Страуструпа) не говорится как создавать глобальные объекты не простых типов (может плохо искал). Приведенный код взял из сети.
Записан
TsysarAndrew
Гость
« Ответ #9 : Январь 19, 2014, 22:31 »

Добавьте метод печатающий ObjectsDispatcher::f_objects и натыкайте его вызовы (ищите где портится)
Я примерно так и делал, и в итоге дошел до такого костыля. Когда в следующий раз вылезет, попробую разобраться более основательно. Сейчас меня беспокоит отставание в проекте тестирования от основного проекта. Жаль, что про инициализацию объектов так никто и не ответил. Может быть это совсем тривиальный вопрос, но он меня постоянно отвлекает, и возможно я не там ищу проблемы.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Январь 20, 2014, 10:13 »

Жаль, что про инициализацию объектов так никто и не ответил. Может быть это совсем тривиальный вопрос, но он меня постоянно отвлекает, и возможно я не там ищу проблемы.
Как минимум один (я) ответил  Улыбающийся Конструктор класса выполняется всегда - это и есть ответ на Ваш вопрос
Записан
TsysarAndrew
Гость
« Ответ #11 : Январь 20, 2014, 22:00 »

С первой проблемой вроде разобрался: я прописал путь к файлам проекта, но не включил сами файлы тестируемого проекта в проект тестирования..
По второй проблеме пока наблюдаю как раз картину создания объекта без конструктора (может вызывается какой-то другой конструктор, который я не перекрыл/объявил?). Все глобальные объекты (ObjectsDispatcher и наследники IObject) содаются как было описано ранее. Получается, что наследник IObject может создаваться раньше ObjectsDispatcher-а, и тогда при вызове метода qObjectsDispatcher.registerObject сам метод вызывается (т.е. объект qObjectsDispatcher создан), но его поле f_objects непроинициализировано, выходит ошибка. С этим пока не разобрался..

« Последнее редактирование: Январь 20, 2014, 22:16 от TsysarAndrew » Записан
Serr500
Гость
« Ответ #12 : Январь 21, 2014, 04:13 »

По второй проблеме пока наблюдаю как раз картину создания объекта без конструктора (может вызывается какой-то другой конструктор, который я не перекрыл/объявил?).
Объект, у которого есть конструктор, не может быть создан без его вызова. На самом деле, конструктор есть у любого объекта, для которого выделяется память. Конструкторы различаются только по спискам параметров, проверьте все ли создания объектов нужного типа вызывают переопределённые конструкторы.

Получается, что наследник IObject может создаваться раньше ObjectsDispatcher-а
При таком объявлении ObjectsDispatcher'а может. Чтобы избежать такой ситуации, не используйте глобальные объекты, их порядок создания отдаётся на откуп компилятору. Если же без глобальных объектов обойтись нельзя, используйте синглтоны с автоматическим созданием объекта при первом обращении.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #13 : Январь 21, 2014, 11:04 »

По второй проблеме пока наблюдаю как раз картину создания объекта без конструктора (может вызывается какой-то другой конструктор, который я не перекрыл/объявил?). Все глобальные объекты (ObjectsDispatcher и наследники IObject) содаются как было описано ранее. Получается, что наследник IObject может создаваться раньше ObjectsDispatcher-а, и тогда при вызове метода qObjectsDispatcher.registerObject сам метод вызывается (т.е. объект qObjectsDispatcher создан), но его поле f_objects непроинициализировано, выходит ошибка. С этим пока не разобрался..
Если глобальные переменные находятся в разных единицах трансляции (cpp файлах), то порядок их инициализации в общем случае не определен.  Можно по-простому собрать их в один cpp файл, тогда они создаются в порядке объявления. Или городить пресловутый синглтон, напр так
Код
C++ (Qt)
class ObjectDispatcher {
public:
 ...
 static ObjectDispatcher & Instance()
 {
   static ObjectDispatcher dispatcher;
   return dispatcher;
 }
};
И использовать ObjectDispatcher::Instance() (переменную qbjectDispatcher убить) 
Записан
TsysarAndrew
Гость
« Ответ #14 : Январь 21, 2014, 16:29 »

Ситуация следующая: есть диспетчер, который содержит информацию о правилах обработки данных. Для каждого правила выделяется отдельный класс. Правил скорее всего будет несколько десятков. В этой ситуации хотелось бы добиться максимальной независимости модулей. Для большинства правил нет необходимости создавать несколько экземпляров.
Было решено сделать это следующим образом: создать почти абстрактный класс-предок для всех правил (IObject), в котором будет реализован только метод регистрации правила в глобальном диспетчере. Все правила-потомки в своих конструкторах вызывают этот метод и так регистрируются в диспетчере. При такой схеме получается только одна межмодульная связь между хедерами iobject.h и objectdispatcher.h, а все все остальные объекты ничего друг о друге не знают (кроме связи наследник-родитель, но от нее никак не избавиться).
Немного поразмыслив над смыслом препроцессорных стражей включения хедеров пришел к выводу, что если в iobject.h будет включен objectdispatcher.h, то в результате перед компиляцией глобальная переменная диспетчера по тексту должна быть выше глобальных переменных правил. Просмотрев исходники вспомнил, что так и хотел сделать с самого начала, но тогда всплыла проблема cross including-а модулей, и я хедер objectdispatcher.h включил не в iobject.h, а в iobject.cpp. Не знаю почему так произошло, т.к. проблема решается просто. Разобрался с cross including-ом, сейчас вроде все работает как нужно.
По результатам этой работы возникает вопрос о возможных подводных камнях такого решения. Также хотелось бы до конца разобраться почему система сразу не выдала исключение о не существовании объекта qObjectsDispatcher, а давала возможность вызвать один из его методов. Если не пропадет желание, то соберу небольшой пример с демонстрацией проблемы и выложу сюда для обсуждения.
« Последнее редактирование: Январь 21, 2014, 16:31 от TsysarAndrew » Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.08 секунд. Запросов: 23.