C++ — Struct c++, qt


Содержание

Сложные типы данных в Си

Структура — это объединение нескольких объектов, возможно, различного типа под одним именем, которое является типом структуры. В качестве объектов могут выступать переменные, массивы, указатели и другие структуры.

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

Общая форма объявления структуры:

После закрывающей фигурной скобки > в объявлении структуры обязательно ставится точка с запятой.

Пример объявления структуры

В указанном примере структура date занимает в памяти 12 байт. Кроме того, указатель *month при инициализации будет началом текстовой строки с названием месяца, размещенной в памяти.

При объявлении структур, их разрешается вкладывать одну в другую.

Инициализация полей структуры

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

  • присвоение значений элементам структуры в процессе объявления переменной, относящейся к типу структуры;
  • присвоение начальных значений элементам структуры с использованием функций ввода-вывода (например, printf() и scanf() ).

В первом способе инициализация осуществляется по следующей форме:

Имя элемента структуры является составным. Для обращения к элементу структуры нужно указать имя структуры и имя самого элемента. Они разделяются точкой:

Второй способ инициализации объектов языка Си с использованием функций ввода-вывода.

Имя структурной переменной может быть указано при объявлении структуры. В этом случае оно размещается после закрывающей фигурной скобки > . Область видимости такой структурной переменной будет определяться местом описания структуры.

Поля приведенной структурной переменной: number.real, number.imag .

Объединения

Объединениями называют сложный тип данных, позволяющий размещать в одном и том же месте оперативной памяти данные различных типов.

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

Когда используется элемент меньшей длины, чем наиболее длинный элемент объединения, то этот элемент использует только часть отведенной памяти. Все элементы объединения хранятся в одной и той же области памяти, начиная с одного адреса.

Общая форма объявления объединения

Объединения применяются для следующих целей:

  • для инициализации объекта, если в каждый момент времени только один из многих объектов является активным;
  • для интерпретации представления одного типа данных в виде другого типа.

Например, удобно использовать объединения, когда необходимо вещественное число типа float представить в виде совокупности байтов

Пример Поменять местами два младших байта во введенном числе

Битовые поля

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

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


При работе с битовыми полями нужно внимательно следить за тем, чтобы значение переменной не потребовало памяти больше, чем под неё выделено.

Пример Разработать программу, осуществляющую упаковку даты в формат


Массивы структур

Работа с массивами структур аналогична работе со статическими массивами других типов данных.

Пример Библиотека из 3 книг

Указатели на структуры

Доступ к элементам структуры или объединения можно осуществить с помощью указателей. Для этого необходимо инициализировать указатель адресом структуры или объединения.

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

struct (C++) struct (C++)

Структуры ключевое слово определяет тип структуры или переменную типа структуры. The struct keyword defines a structure type and/or a variable of a structure type.

Синтаксис Syntax

Параметры Parameters

шаблон спецификации template-spec
Необязательные спецификации шаблона. Optional template specifications. Дополнительные сведения см. спецификации шаблона. For more information, refer to Template Specifications.

struct struct
Структуры ключевое слово. The struct keyword.

ms-decl-spec ms-decl-spec
Необязательная спецификация класса хранения. Optional storage-class specification. Дополнительные сведения см. __declspec ключевое слово. For more information, refer to the __declspec keyword.

Тег tag
Имя типа, присваиваемое структуре. The type name given to the structure. Тег становится зарезервированным ключевым словом в области структуры. The tag becomes a reserved word within the scope of the structure. Тег является необязательным. The tag is optional. Если он опущен, определяется анонимная структура. If omitted, an anonymous structure is defined. Дополнительные сведения см. в разделе типы анонимных классов. For more information, see Anonymous Class Types.

базовый список base-list
Необязательный список классов или структур, из которых эта структура будет наследовать члены. Optional list of classes or structures this structure will derive its members from. См. в разделе базовых классов Дополнительные сведения. See Base Classes for more information. Каждый базовое имя класса или структуры может предшествовать спецификатор доступа (открытый, частного, защищенные) и виртуального Ключевое слово. Each base class or structure name can be preceded by an access specifier (public, private, protected) and the virtual keyword. См. в таблице доступа к членам в управление доступом к членам класса Дополнительные сведения. See the member-access table in Controlling Access to Class Members for more information.

Список членов member-list
Список членов структуры. List of structure members. Ссылаться на Обзор членов класса Дополнительные сведения. Refer to Class Member Overview for more information. Единственное отличие заключается в том, структуры используется вместо класс. The only difference here is that struct is used in place of class.

деклараторы declarators
Список деклараторов с указанием имени структуры. Declarator list specifying the names of the structure. В списках деклараторов объявляются один или несколько экземпляров типа структуры. Declarator lists declare one or more instances of the structure type. Деклараторы могут содержать списки инициализаторов, если все данные-члены структуры являются открытый. Declarators may include initializer lists if all data members of the structure are public. Списки инициализаторов распространены в структурах, так как данные-члены являются открытый по умолчанию. Initializer lists are common in structures because data members are public by default. См. в разделе Обзор из деклараторы Дополнительные сведения. See Overview of Declarators for more information.

Примечания Remarks

Тип структуры — это пользовательский составной тип. A structure type is a user-defined composite type. Он состоит из полей или членов, которые могут иметь разные типы. It is composed of fields or members that can have different types.

В C++ структура совпадает со значением класса за исключением того, что ее члены являются открытыми открытый по умолчанию. In C++, a structure is the same as a class except that its members are public by default.

Сведения об управляемых классах и структурах в C++выполняет, см. в разделе классы и структуры. For information on managed classes and structs in C++/CLI, see Classes and Structs.

Использование структуры Using a Structure

В C, необходимо явно использовать структуры ключевого слова для объявления структуры. In C, you must explicitly use the struct keyword to declare a structure. В C++, вам не обязательно должны использовать структуры ключевое слово после определения типа. In C++, you do not need to use the struct keyword after the type has been defined.

Если тип структуры определен путем размещения одной или нескольких разделенных запятыми имен переменных между закрывающей фигурной скобкой и точкой с запятой, имеется возможность объявления переменных. You have the option of declaring variables when the structure type is defined by placing one or more comma-separated variable names between the closing brace and the semicolon.

Переменные структуры можно инициализировать. Structure variables can be initialized. Инициализация каждой переменной должна быть заключена в скобки. The initialization for each variable must be enclosed in braces.

Дополнительные сведения см. в разделе класс, объединение, и перечисления. For related information, see class, union, and enum.

Ожидаемый ‘,’ или ‘. ‘ перед ‘struct’ error

У меня в первый раз такая ошибка.

Я хотел бы сохранить эту вложенную структуру в потоке. Итак,

1) ошибка: ожидается ‘,’ или ‘. ‘ перед ‘struct’QDataStream & operator Dmitry Sazonov ответил: 20 апреля 2020 в 07:25

2 ответа

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


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

и очень расстроен.

Я нашел неправильное место, это имя переменной»interface» (от маленькой буквы). Переименовав ее в i , например очистите код.

Тип Interface объявлен в пространстве имен, поэтому изменение его имени не принесло результата.

Начинаем программировать на Qt

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

Hello Notepad

В первом примере, мы создадим простое окно для редактирования текста. Это самая элементарная программа с графическим интерфейсом Qt.

Исходный код примера:

Рассмотрим подробно каждую строчку кода. В первых двух, мы подключаем заголовочные файлы классов QApplication и QTextEdit, которые необходимы для работы этого примера. Каждому классу в Qt соответствует заголовочный файл с таким же названием.

В строке 6 создается объект класса QApplication, который управляет основными ресурсами, необходимыми для запуска любой программы с графическим интерфейсом Qt. Ему необходимо передать аргументы argv и args функции main() , т.к. Qt использует некоторые параметры командной строки, передаваемые при запуске программы.

В восьмой строке кода создается объект класса QTextEdit. QTextEdit — это визуальный элемент графического интерфейса. В Qt используются определенные виджеты — например, полосы прокрутки, метки и кнопки radio. Виджет может быть контейнером для хранения других виджетов. Наглядным примером является главное диалоговое окно программы.

В строке 9, окно редактирования текста выводится на экран в главном фрейме программы. Поскольку виджеты могут работать как контейнеры (В экземпляре класса QMainWindow находятся полосы панели инструментов, меню, строка состояния и несколько других виджетов), мы может отобразить только один виджет в окне нашей программы. По умолчанию, виджеты не видны. Функция show() используется для их отображения.

В строке 11, создается объект QApplication, который генерирует цикл событий в процессе работы приложения. События генерируются и передаются на обработку виджетам. Примерами событий могут являться клики мыши, нажатия клавиш на клавиатуре и т.п. Когда вы вводите текст в окне редактирования виджета QTextEdit, нажатия клавиш обрабатываются средствами виджета, и вводимый текст отображается в процессе набора.

Цукерберг рекомендует:  Введение в разработку игр на Pygame. Хотели написать игру Время пришло!

Для запуска программы, откройте командную строку и зайдите в директорию с .cpp файлом программы. Выполните следующие команды shell-интерпретатора, чтобы скомпилировать пример.

После успешного выполнения предыдущих команд, скомпилированная программа будет размещена в директории текущего проекта (в Windows вы можете использовать nmake вместо make . Исполняемые файлы будут размещены в директориях debug или release, которые создадутся командой make . qmake — это утилита, которая создает файлы конфигурации Qt-проекта, если ей передан аргумент -project . После создания файла конфигурации (.pro), qmake генерирует Makefile, который используется утилитой make для сборки приложения. Позже, мы рассмотрим процесс написания собственных .pro файлов.

Добавления кнопки выхода

В реальных проектах, обычно используется более одного виджета. Сегодня мы добавим кнопку QPushButton под полем редактирования текста. При клике на нее, программа будет завершаться.

Рассмотрим исходный код программы.

Сначала подключается заголовочный файл QtGui. В нем содержатся все классы графического интерфейса Qt.

В строке 10 применяется механизм сигналов и слотов, для того, чтобы закрыть программу после нажатия на кнопку выхода. Слот — это функция, которая может быть вызвана в процессе выполнения программы. Сигнал — функция, вызывающая функции-слоты, которые с ним посредством QObject::connect.

quit()слот класса QApplication, завершающий работу приложения. clicked() — сигнал, который передает нажатая кнопка QPushButton. Статическая функция QObject::connect связывает определенный слот и сигнал. SIGNAL() и SLOT() — макросы, которые принимают сигнатуры сигналов и слотов, для их связи между собой. Также, функции connect() необходимо передать указатели на объекты, которые будут принимать и рассылать сигналы.

В строке 12 создается объект класса QVBoxLayout. Как уже было сказано, виджеты могут содержать в себе другие виджеты. Можно задавать координаты и размер вложенных виджетов непосредственно, но обычно для этого используют слои (layouts). Слой управляет границами вложенных виджетов. Например, объект QVBoxLayout размещает виджеты в одну вертикальную колонку.

В строках 13 и 14, мы добавляем поле редактирования текста и кнопку выхода к слою layout . В строке 17 задается главный слой для всего приложения.

Наследование QWidget

Когда пользователь хочет закрыть приложение, ему можно показать всплывающее окно, в котором программа спросит подтверждение данного действия. В этом примере мы создадим наследник QWidget и добавим в него слот, который будет привязан к кнопке выхода.

Рассмотрим следующий код.

Макрос Q_OBJECT объявляет наш класс как QObject. Он должен находиться в самом начале определения класса. QObject добавляет несколько расширенных возможностей к обычному классу C++. Например, имена классов и слотов можно запросить в процессе выполнения. Также, мы можем узнать типы параметров слота и вызвать его.

В строке 13 объявляется слот quit() . В последствии, мы присоединим этот слот к сигналу.

В предыдущих примерах, создание графики и присоединение слотов осуществлялось внутри функции main() . Сейчас мы будем использовать конструктор Notepad.

Как видно из определения класса, мы используем указатели на объекты textEdit и quitButton . Для объектов QObject, почти всегда рациональнее выделять память в куче, вместо их копирования.


Мы используем функцию tr() для обработки всех строк, которые видны пользователю. Эта функция применяется, когда приложение нужно перевести на несколько языков. Здесь мы не будем углубляться в детали, но вы можете найти нужную информацию в описании библиотеки Qt Linguist.

Создание файлов .pro

Ранее, мы использовали команду qmake -project для создания файлов .pro. В следующем примере, мы создадим этот файл вручную.

Теперь для сборки программ на Qt, будут использоваться следующие команды.

Использование QMainWindow

Во многих приложениях удобно использовать QMainWindow — слой, в который можно добавлять контекстные меню, встроенные виджеты, панели инструментов и строку состояния. В QMainWindow есть центральная область, куда можно добавлять любые дочерние виджеты. В этом примере мы разместим там поле для редактирования текста.

Рассмотрим определение класса Notepad .

Мы создали два дополнительных слота — open() и save() . Они будут открывать и сохранять документ. Пример их реализации будет представлен позже.

Очень часто, один и тот же слот используется одновременно несколькими виджетами. Например, в пунктах меню и соответственных кнопках на панели инструментов. Чтобы упростить этот процесс, в Qt существует класс QAction, который можно передавать сразу нескольким виджетам и присоединять к слоту. Например, QMenu и QToolBar могут создавать пункты меню, используя одно действие QAction. Скоро вы убедитесь, как это облегчает процессе разработки.

Сначала, мы используем конструктор класса Notepad для создания графического интерфейса программы.

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

Когда пользователь кликает на пункте меню, рассылается сигнал действия и вызывается соответствующий слот.

Сохранение и открытие

В этом примере, мы реализуем функционал слотов open() и save() , которые были добавлены в предыдущем примере.

Первый шаг — это запрос у пользователя имени файла для открытия. В состав Qt входит класс QFileDialog — диалоговое окно, в котором пользователь может выбрать файл. Статическая функция getOpenFileName() открывает модальное окно для выбора файла и возвращает путь к выбранном файлу. Если пользователь отменил действие, функция возвращает пустую строку.

После того, как было получено имя файла, мы пробуем открыть его с помощью функции open(). Она возвращает логическое true в случае успешного завершения. Если файл открыть не удалось, мы используем класс QMessageBox для того, чтобы показать пользователю всплывающее окно с сообщением об ошибке (смотрите описание класса QMessageBox, для получения более подробной информации).

Чтение данных становится тривиальной задачей, благодаря классу QTextStream — оберткой над объектом QFile. Функция readAll() возвращает содержимое файла, как объект QString. Это содержимое мы поместим в поле для редактирования текста. Затем, мы закрываем файл с помощью функции close(), чтобы вернуть файловый дескриптор операционной системе.

Перейдем к реализации слота save() .

Для сохранения данных мы снова используем класс QTextStream. При помощи него, можно записывать строки QString в файл, используя оператор .

Структурный тип данных

Объявление и определение структур

Примером структуры может послужить любой объект, для которого описывается ряд его характеристик, имеющих значение в данной программе. Например, для книг это может быть название, автор, количество страниц; для окружности — координаты центра, диаметр, цвет. На языке программирования C объявление вышеупомянутых структурных типов данных может выглядеть так:

В данном случае мы как бы создаем новый тип данных, но еще не объявляем переменных этих типов. Обратите внимание на точку с запятой в конце объявлений.

Чаще переменные структур объявляются так:

Здесь объявляются три структуры типа circle и одна структура типа book. Можно объявлять типы структур и их переменные по-иному, но мы для избежания путаницы рассматривать другие способы не будем.

Каждая переменная типа circle содержит четыре элемента (или поля) — x, y, dia, color. Можно сказать, что они представляют собой вложенные переменные. Причем эти переменные разных типов. Таким образом переменная-структура позволяет объединить под одним именем ряд разнородных данных. Обычно это нужно для удобства обработки данных. Если нельзя было бы создавать структуры, то пришлось бы создавать множество независимых переменных или ряд массивов, явной взаимосвязи между которыми не было бы. Структуру же позволяют объединять взаимосвязанные данные. Это конечно еще не объектно-ориентированное программирование, но уже взгляд в его сторону.

Объявив переменную структурного типа, мы можем получить доступ к каждому ее элементу для присваивания, изменения или получения значения:

Значение элементов структуры можно сразу определять при объявлении переменной, что похоже на инициализацию массивов:

Значение переменной-структуры можно присвоить переменной того же типа:

В четвертой строке кода данные переменной old присваиваются new. В итоге вторая структура содержит копию данных первой. То, что можно выполнять присваивание по отдельным полям должно быть понятно.

Структуры и функции

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


Примечание. При компиляции программы в GNU/Linux команда выглядит так: gcc program.c -lm . Это связано с использованием библиотеки с математическими функциями.

  • Объявляется структура circle как глобальный тип данных. Таким образом любая, а не только main() , функция может создавать переменные этого типа.
  • Функция new_circle() возвращает структуру, а функция cross() принимает структуру по значению. Следует отметить, что можно создавать функции, которые как принимают (возможно, несколько структур) так и возвращают структуру.
  • В функции new_circle() создается переменная new типа struct circle, поля которой заполняются пользователем. Функция возвращает значение переменной new в функцию main() , где это значение присваивается переменной a, которая также принадлежит типу sctruct circle.
  • Функция cross() определяет, пересекает ли круг начало координат. В ее теле вычисляется расстояние от центра круга до начала координат. Это расстояние является гипотенузой прямоугольного треугольника, длина катетов которого равна значениям x и у. Далее, если гипотенуза меньше радиуса, то круг пересекает начало координат, т.е. точку (0, 0).
  • В функции main() при вызове cross() данные, содержащиеся в переменной a, копируются и присваиваются переменной c.

Указатели и структуры

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

Тогда функция reader() должна иметь примерно такое объявление:
void reader (struct book *pb);

Возникает вопрос, как при использовании указателей обращаться к элементам структур? Во первых надо получить саму структуру, т.е. если pnew указатель, то сама структура будет *pnew . Далее можно уже обращаться к полям через точку: *pnew.title . Однако это выражение не верно, т.к. приоритет операции «точка» (обращение к полю) выше операции «звездочка» (получить значение по адресу). Таким образом приведенная запись сначала пытается получить значение поля title у указателя pnew, но у pnew нет такого поля. Проблема решается с помощью скобок, которые изменяют порядок выполнения операций: (*pnew).title . В таком случае сначала извлекается значение по адресу pnew, это значение является структурой. Затем происходит обращение к полю структуры.

В языке программирования C записи типа (*pnew).title часто заменяют на такие: pnew->title , что позволяет синтаксис языка. Когда в программе вы видите стрелку (тире и скобка) всегда помните, то, что написано до стрелки, — это указатель на структуру, а не переменная-структура.

Пример кода с использованием указателей:

Массивы структур

Обычно создание в программе одной переменной структурного типа не имеет особого смысла. Чаще структурами пользуются, когда необходимо описать множество похожих объектов, имеющих разные значения признаков. Значения каждого объекта следует объединить вместе (в структуру) и тем самым отделить от значений других объектов. Например, описание ряда книг или множества людей. Таким образом мы можем организовать массив, где каждый элемент представляет собой отдельную структуру, а все элементы принадлежат одному и тому же структурному типу.

Напишем программу для учета персональных компьютеров в организации. Каждая структура будет описывать определенные модели и содержать поле, в котором будет указано количество таких объектов. Поэтому при объявлении структурного типа данных следует описать такие поля как тип компьютера, модель процессора, количество.

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

  • Массив структур инициализируется при его объявлении.
  • Функции viewer() и changer() принимают указатели на структуру computer.
  • В теле viewer() указатель инкрементируется в заголовке цикла; таким образом указывая на следующий элемент массива, т.е. на следующую структуру.
  • В выражении (comp+i)->qty скобки необходимы, т.к оператор -> имеет более высокий приоритет. Скобки позволяют сначала получить указатель на i-ый элемент массива, а потом обратиться к его полю.
  • Декрементирование i в функции changer() связано с тем, что индексация начинается с нуля, а номера элементов массива, которые пользователь видит на экране, с единицы.
  • Для того, чтобы уменьшить количество компьютеров, при запросе надо ввести отрицательное число.
Цукерберг рекомендует:  Php - При заходе в phpmyadmin скачивается файл

Пример результата работы программы:

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

Передача структуры сигнала в Qt

Я хочу отправить структуру с сигналом в Qt. Как я могу это сделать? Я знаю, как отправлять целые числа, строки, изображения и т.д. С сигналом, но путаться со структурой. Я прочитал некоторые сообщения и узнал о Q_DECLARE_METATYPE(), но я не понимаю, как его использовать.

Ошибки, которые я получаю 1. Необязательная специализация в классе пространства имен без пространства имен. 2.Специализация шаблона должна появляться в области пространства имен 3. struct QMetaTypeId обновляется с другим доступом. Может кто-нибудь, пожалуйста, скажите мне, где я ошибаюсь.

Ошибки состоят в том, что использование Q_DECLARE_METATYPE находится внутри вашего объявления класса. Он должен находиться вне любых классов или пространств имен. Вам просто нужно переместить его так:

Как вы уже узнали, вы должны использовать Q_DECLARE_METATYPE , чтобы сделать тип известным Qt.

Кроме того, вам может потребоваться вызвать qRegisterMetaType . В частности, эту функцию нужно вызывать перед использованием структуры в очереди с сигналом/слотом или перед ее использованием с помощью QObject:: property() API.

Рабочий пример

standarddata.h


main.cpp

Trick со статической инициализацией

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

standarddata.h

standarddata.cpp

Q_GADGET

В качестве альтернативы вы можете использовать Q_GADGET вместо Q_DECLARE_METATYPE :

Структура Qt-проекта на C++

Введение

Qt сегодня стал крайне популярным SDK для разработки кросс-платформенных приложений. И это легко понять. Обеспечена поддержка всех основных операционных систем: *nix, Windows и MacOS, а мощные возможности библиотек позволяют решать даже сложные задачи минимальным количеством кода. Кроме того, организация проекта на базе pro-файла весьма удобна, поэтому я применяю ее даже для тех проектов на C/C++, которые напрямую не используют возможности Qt’а. О таком способе организации проектов мы и поговорим.

Реклама

Дерево проекта

Начнем сразу с общей структуры всего проекта с последовательным разбором его элементов:

На верхнем уровне у нас расположено 8 каталогов. Разберем каждый из них в отдельности.

Каталог с бинарниками — bin/

Сюда будут складываться все наши исполняемые файлы. Для режимов сборки debug/ и release/ предусмотрены собственные подкаталоги, чтобы упростить переключение между отладочной и чистовой версиями.

Каталог сборки — build/

Чтобы не смешивать вспомогательные obj , moc , rcc и ui файлы с исходниками или готовыми бинарниками отведен отдельный каталог. По аналогии с bin/ он разбит на подкаталоги debug/ и release/ для соответствующих режимов сборки.

Каталог с заголовочными файлами сторонних библиотек — import/

Сейчас довольно сложно написать более или менее серьезное приложение, в котором не задействованы никакие сторонние библиотеки. Конечно, всегда есть вариант разместить эти зависимости в системных папках и настроить к ним пути, но это решение имеет множество недостатков. Лучше всегда держать все зависимости проекта в едином каталоге, который будет помещен под управление системы контроля версий, однако это уже тема отдельной заметки. Организация этого подкаталога может быть достаточно произвольной, но я предпочитаю для каждой отдельной библиотеки выделить свой подкаталог с h -файлами, чтобы избежать возможного конфликта имен.

Каталог с внешними заголовочными файлами нашего проекта — include/

Сюда мы будем помещать все наши h -файлы, которые соответствуют открытым частям интерфейса библиотек проекта. Вообще, этот каталог можно было бы назвать не include/ , а export/ , но это уже дело вкуса. Он вполне может оказаться пустым, если вы пишите небольшой одноразовый проект, код которого потом не будет повторно использоваться. Но если вы решите передать кому-то ваши наработки, то при наличии такого каталога для этого будет достаточно скопировать его вместе с содержимым lib.*/ , о котором мы поговорим в следующем подразделе.

Каталог с библиотеками — lib.(linux|win32)/

Чтобы не плодить глубокую вложенную иерархию подкаталогов с ветвлением по версиям операционных систем, мы просто создаем необходимые каталоги верхнего уровня с нужным суффиксом linux или win32 . Я не занимаюсь разработкой приложений для MacOS, но думаю, что вы без труда добавите нужный суффикс, если это понадобится. Сюда мы будем помещать как сторонние библиотеки с разбиением на подкаталоги по аналогии с заголовочными файлами в import/ , так и наши собственные библиотеки, но уже непосредственно в сам каталог lib.*/ .

Кроме того, заметим, что для win32-приложений, собираемых с помощью компилятора msvc из Visual Studio, динамические библиотеки разделяются на *.lib и *.dll файлы. Первые используются во время линковки компоновщиком, а вторые непосредственно во время работы приложения и должны находиться в одном каталоге с использующим их исполняемым файлом. Возникает вопрос о том, куда поместить эти файлы для используемых сторонних библиотек. Однозначно *.lib -файлы должны лежать по путям, параллельным *.so -шникам для линукс приложений. Но куда деть *.dll -ки? Возможно несколько вариантов. Один из них заключается в том, чтобы поместить их рядом с *.lib -файлами. Но тогда их придется вручную копировать в bin/ . Если же поместить их сразу в bin/ , то они будут засорять сборку под другими операционными системами или с компилятором gcc, поэтому я бы не стал рекомендовать этот способ. Отдельный каталог для этого заводить тоже смысла нет, поэтому с учетом всех плюсов и минусов я сам храню *.dll -файлы рядом с *.lib -ами.

Каталог с исходниками — src/

В нем для каждого модуля заводится отдельный подкаталог с его именем, в котором будут лежать cpp -файлы и закрытые h -файлы, которые нужны только внутри этого модуля. Не забудьте про каталог include/ верхнего уровня, в который идет на экспорт часть внешних заголовочных файлов нашего приложения. Но что делать с разделяемыми заголовочными файлами, которые нужны в нескольких наших модулях, но не имеющих такого большого значения, чтобы можно было их экспортировать? Для этого предназначен внутренний каталог src/include/ . В него мы можем поместить наборы внутренних констант, объявлений классов и функций, а потом совместно использовать их в наших модулях, не нарушая инкапсуляцию.

Каталог с тестами — tests/

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

Реклама

Создаем файлы проекта

А теперь посмотрим на файлы, определяющие структуру проекта для утилиты qmake , на основе которых будет создан набор Makefile ‘ов. Как говорилось во введении, утилита qmake позволяет управлять проектами для любых C/C++ приложений, поэтому если вы пишите программу на C++, но по какой-то причине для разработки графического интерфейса применяете модули GTK+ или Win32 API, то вас не должно это останавливать от применения приведенного ниже материала.


MyProject.pro

Главный pro -файл нашего проекта. В Visual Studio этот уровень называется Solution.

Для него используется Qt-шаблон subdirs , что означает, что наш проект будет состоять из набора модулей-подпроектов. Кто-то может решить, что нет смысла заниматься компоновкой такой многоуровневой структуры и гораздо удобнее сделать приложение на базе единственного app -проекта. Возможно, что в некоторых случаях это так, но шаблон subdirs не запрещает использование одного модуля, да и много времени вы на этом не сэкономите. Зато в дальнейшем очень часто оказывается, что одного модуля было недостаточно и все равно приходится что-то менять.

В приведенном примере у нас всего два модуля: MyApp — исполняемое приложение и MyLib — вспомогательная библиотека. Но прежде чем спуститься на уровень ниже и посмотреть на то, как устроены MyApp и MyLib , рассмотрим несколько вспомогательных pri -файлов.

common.pri

Общий для всех модулей файл с определениями путей и некоторых констант, задействованных при сборке:

Разберем отдельные блоки этого файла. В первой строке просто фиксируется путь к корневому каталогу проекта, относительно которого будем определять все остальные пути:

Далее определяем то, под какой ОС происходит сборка, и устанавливаем соответствующим образом значение суффикса OS_SUFFIX . Значение этого суффикса будет использовано для ветвления по каталогам lib.*/ .

В следующем фрагменте в зависимости от режима сборки ( debug или release ) определяется значение BUILD_FLAG , которое будет указывать на версию используемого подкаталога в bin/ и build/ :

Кроме того, определяется вспомогательный суффикс LIB_SUFFIX . Мы будем использовать его для того, чтобы к именам библиотек в отладочном режиме присоединялся символ d . За счет этого мы можем иметь единый каталог для библиотек и не допускать конфликтов имен. Например, в lib.win32/ у нас может одновременно находиться обе версии MyLib.lib и MyLibd.lib .

Далее по порядку определяются пути к библиотекам lib.*/ , к открытым заголовочным файлам include/ , к импортируемым заголовочным файлам import/ и путь к каталогу с бинарниками bin/ :

Заметим, что в конце определения LIBS_PATH мы воспользовались нашим OS_SUFFIX , а в конце BIN_PATH подставили BUILD_FLAG , чтобы привести пути в соответствие с нашей начальной задумкой по ветвлению конфигурации проекта на основании версии ОС и режиму сборки.

Ниже стоит блок, который задает пути сборки для файлов ресурсов rcc , файлов графического интерфейса ui , МОК-файлов moc и объектных файлов obj :

Каталог сборки для каждого подпроекта будет свой. При этом его расположение зависит от режима сборки и от имени самого подпроекта, которому соответствует переменная TARGET , определенная для каждого модуля.

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

Ключ -L перед $$ означает, что мы определяем каталог, в котором компоновщик должен искать библиотеки в процессе сборки. А чтобы добавить конкретную библиотеку, нужно использовать ключ -l . Например:

При этом, как видно из примера, расширение файла указывать не требуется, поскольку в разных ОС и для разных компиляторов оно может быть разным.

Последняя строка не является обязательной для сборки, но если вы хотите задействовать в вашем приложении возможности C++11, то имеет смысл ее не забыть:

app.pri

Между всеми исполняемыми модулями есть что-то общее. Определим соответствующие настройки сборки в pri -файле:

Переменная DESTDIR указывает путь, в который будет помещен готовый исполняемый файл. Это окажется либо bin/debug/ , либо bin/release/ .

В следующей строке определяется путь поиска динамических библиотек по умолчанию. В Windows он работать не будет. А в Linux позволяет упростить запуск скомпонованного исполняемого файла. Если опустить эту строку, то приложение все равно можно будет запустить, но тогда:

  1. Либо библиотеки должны лежать в системных папках, по которым осуществляется поиск;
  2. Либо путь сборки библиотек должен быть добавлен к системным с помощью файла /etc/ld.so.conf ;
  3. Либо должна быть определена переменная окружения LD_LIBRARY_PATH .

Вариант с LD_LIBRARY_PATH является самым простым, поскольку в этом случае вам не нужны root -права в системе. Удобно использовать для этого скрипт следующего вида:

lib.pri

Как и для исполняемых файлов, для библиотек тоже удобно определить общий pri -файл:

Переменная DESTDIR имеет такой же смысл, как и в app.pri .

Цукерберг рекомендует:  Хакинг - Взлом мозга

Следующая строка будет работать только в Windows. Она удобна тем, что позволяет автоматически скопировать все *.dll -файлы в каталог к исполняемым файлам.

Определения переменных в конце указывают информацию о версии библиотеки и ваш копирайт. Например, в Linux при значении VERSION = 2.0.1 вы получите библиотеки с именем вида libMyLib.so.2.0.1 . Но копирайт будет отображаться только в Windows, при этом имя библиотек будет выглядеть следующим образом: MyLib2.dll , а в свойствах вы увидите что-то подобное:

Заметим, что может появиться необходимость определить переменную VERSION для каждой библиотеки отдельно, если вы хотите иметь тонкий контроль над этим значением. Но я считаю, что версии всех библиотек в рамках одного проекта должны совпадать и не имеет смысла вносить излишние различия.


Кроме того, обратите внимание, что в описании библиотеки указаны не только «Авторские права», но и «Описание файла» с «Названием продукта». Эти два значения уже имеет смысл определять для каждой библиотеки в отдельности.

MyApp.pro

Пришло время посмотреть на содержимое файла конкретного модуля:

Содержимое этого файла достаточно типично для Qt-проектов и не вызывает особых сложностей. Большую его часть может легко создать QtCreator , поэтому рассмотрим лишь последние 3 строки. Директива include позволяет включить содержимое наших pri -файлов, объявленных ранее. В зависимости от версии утилиты qmake мы могли бы вынести команду include( ../../common.pri ) в начало файла app.pri , чтобы уменьшить количество кода, поэтому проверьте, будет ли работать такой вариант у вас. В последней строке мы просто подключаем наш модуль MyLib с суффиксом LIB_SUFFIX . Заметим, что путь поиска библиотек компоновщиком уже был определен в common.pri , поэтому здесь нам его дублировать не нужно.

MyLib.pro

Здесь тоже все достаточно стандартно, но обратим внимание на следующие моменты:

  1. Чтобы в переменной TARGET задействовать суффикс LIB_SUFFIX из common.pri подключаем его заранее в самом начале;
  2. Заголовочные файлы предполагается передать в дальнейшем кому-то другому, поэтому перенесем их в предназначенный для этого каталог include/ ;
  3. Название продукта и описание, про которые говорилось выше, мы описываем для каждой библиотеки с помощью переменных QMAKE_TARGET_PRODUCT и QMAKE_TARGET_DESCRIPTION .

Заключение

Вот мы и рассмотрели способ организации проекта на C++. Очевидно, что нет особой необходимости пользоваться утилитой qmake для того, чтобы ей следовать. Кроме того, с минимальными изменениями она вполне может подойти и для проектов на других языках программирования. Однако следует учитывать, что не существует идеального способа организации проекта, поэтому вы можете взять предложенный мной вариант в качестве основы и адаптировать его под свои нужды.

Qt: Write Struct to File

Я боюсь писать struct в файл через QFile в Qt.

DataPacket структура заполняется в другом месте:

Когда я проверить файл outputFile журнала, $GPGSA. строка не где видно. Если я после этого дополнительно записать payload поле отдельно:

$GPGSA строка затем записывается правильно.

я что-то отсутствует .

Создан 18 сен. 17 2020-09-18 15:57:00 weblar83

‘sizeof (DataPacket)’ содержит только целые элементы и указатель, а не фактическую строку. Попробуйте использовать сериализацию Qt, но сначала замените ‘char *’ на ‘QString’. Участник членства Qt serialization — это все, что вам нужно для этого: http://doc.qt.io/qt-5/qdatastream.html – AlexanderVX 18 сен. 17 2020-09-18 16:01:10

Попробуйте использовать это: const char * str = dataString.toStdString(). C_str(); QByteArray arr = QByteArray :: fromRawData (str, sizeof (str)); – Simon 18 сен. 17 2020-09-18 16:04:37

Вы работали над моим решением? – eyllanesc 01 ноя. 17 2020-11-01 14:14:47

1 ответ

Другое дело, что держать Qt совместимости переименовать некоторые собственные типы, поэтому я рекомендую использовать эквивалентные типы, в дополнение к использованию QString непосредственно вместо полукокса * полезной нагрузки и payloadLength как показано ниже:

также при написании его лучше использовать QDataStream , как показано ниже:

Follo Пример крыла показывает его использование:

Примечание: QDataStream устанавливает payloadLength косвенно, потому что она знает размер QString.

Создан 18 сен. 17 2020-09-18 17:21:22 eyllanesc

QT, C++, fm

Возникла необходимость написать на QT3 простенький файловый менеджер.
Прошу, не надо флеймить и пытаться меня оскорбить.

Я имею некоторые навыки программирования на С++ под линукс. С QT тоже работал. Не на том уровне, на котором хотелось бы.

Посоветуете русские доки по следующим темам:
— работа с файлами/директориями (вроде у QT средства есть хорошие)
— Обмен сообщениями между приложениями (имеются ввиду несколько экземпляров приложений. Так же интересно как создать область памяти, к которой имеют доступ несколько приложений)

Про QDirectories (кажется) и прочие классы я читал в doc-qt, но списка методов и полей мало.

Посоветуйте что нибудь с РЕАЛЬНЫМИ примерами.
Заранее благодарю.


Re: QT, C++, fm

Re: QT, C++, fm

Ну, во первых интерфейс. Можно сделать простенький QVBox, либо QMainWindow с тулбаром, статусбаром и директориями внутри — это как тебе удобней :)

Далее лиситнг каталогов. Скажу сразу, что FAM QT не поддерживает, значит следить в реальном времени за каталогами не получится (в отличие от KIconView). Для листинга калогов можно использовать QDir, все найденные файлы запихивать в QIconView или QListView. В QIconView с помощью сигналов pressed, clicked, etc. отслеживай клики по файлам.

Вот вроде и всё :)

P.S. полистай пример из дистриба qt/examples/fileiconview

Re: QT, C++, fm

>> Обмен сообщениями между приложениями

Эмм. можно с помощью сокета, как XMMS. Но полноценного обмена а-ля DCOP наврятли .

Можно ещё попробовать через D-BUS :)

Re: QT, C++, fm

to vasirck: Будем изучать.

to Krasu: Спасибо, учту.

Re: QT, C++, fm

Re: QT, C++, fm

У пользователя должен быть выбор, просматривать файлы как список или таблицу.
Поэтому при работе с прогой ?придется использовать указатель на void?. Но ведь при работе везде нужно будет использовать следующий прием:
switch (config->modeview) <
case «QListView»:
<
(QListView)config->list;
.
>
case «QIconView»:
<
(QIconView)config->list;
.
>
>
Понимаю что я бред написал. Не в курсе у какого нибудь виджета(QListBox, QIconView, QlistView etc) есть возможность выбирать способ представления?
Я хотел бы узнать что вы думаете о «алгоритме»:

Сначала в контейнер забиваем объекты TburzumFile(класс описан мною, имя конечно другое), имеется указатель на Q. Item.
Пользователь кликает или еще что на Q. Item, ищем по имени в контейнере объект, производим необходимые действия.

Какой контейнер использовать? Я работал только с вектором, приимуществ других не знаю.

Re: QT, C++, fm

Можно сделать наследника QWidgetStack, скажем BurzumWidgetStack. добавляешь в него первым ваджетом BurzumIconView, вторым — BurzumListView. На тулбар выносишь ссответсвенно две иконки — одна будет при нажатии выводить BurzumIconView (QWidget::raiseWidget(..)), вторая — BurzumListView.

В _каждом_ View очевидно надо ловить сигналы clicked, pressed, etc. — составлять полный путь для выбранного файла, и делать с ним что-нибудь (копировать, удалять, etc.)

Re: QT, C++, fm

Что есть QWidgetStack? Что значит добавить ПЕРВЫМ BurzumIconView, ВТОРЫМ BurzumListView? Массив какой то получается.

Чем хуже будет так?

struct TLists <
QListView list;
QIconView icon;
>;

Т.Е. в памяти придется держать два или больше *view’ов.
Не будет ли это проблематично при больших списках файлов?
Можно их параллельно обрабатывать, или при переключении модов.

PS Спасибо что серьезно отнесся к моей затеи.

Re: QT, C++, fm

>>Что есть QWidgetStack?

[The QWidgetStack class provides a stack of widgets of which only the top widget is user-visible.

The application programmer can move any widget to the top of the stack at any time using raiseWidget(), and add or remove widgets using addWidget() and removeWidget(). It is not sufficient to pass the widget stack as parent to a widget which should be inserted into the widgetstack.]


>>struct TLists<
>>QListView list;
>>QIconView icon;
>>>;

Это как ? Не понял :) QIconView и QListView — это _ваджеты_, а не просто списки файлов. Вообзем, читай assistant.

Проблемы ? Да могут быть, но маловероятно. Даже

5000 QIconViewItem кушают не так уж и много памяти :)

Тэкс . «Красный дракон» начинается :))

Re: QT, C++, fm

Я имел в виду это:

struct TLists <
QListView *list;
QIconView *icon;
>;

В общем, не обращай внимания. Постораюсь больше не задавать глупых вопросов.

Re: QT, C++, fm

>>struct TLists<
>>QListView *list;
>>QIconView *icon;

Да какая разница :)), просто эти два указателя хранить будешь в BurzumWidgetStack, структкры тут не надо. Вообще, если что — обращайся, мне такой проект будет интересен ;) Чем смогу помогу.

ez code

Просто о сложном.

Структуры в C++

Перед тем как приступить к изучению классов в C++, мы рассмотрим тип данных подобный классу — структуры. Структуры полезны, когда нам надо объединить несколько переменных с разными типами под одним именем. Это делает программу более компактной и более гибкой для внесения изменений. Также структуры незаменимы, когда необходимо сгруппировать некоторые данные, например, запись из базы данных или контакт из книги адресов. В последнем случае структура будет содержать такие данные контакта как имя, адрес, телефон и т.п.

Синтаксис

В процессе написания программы может потребоваться сгруппировать разные данные. Например, вы захотите хранить координаты некоторых объектов и их имена. Вы можете сделать это с помощью массивов:

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

Формат объявления структуры выглядит так:

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

structName — имя структуры, variableName — имя переменной.

x_coor, y_coor и name — поля нашей структуры. При объявлении структуры мы создаем составной тип данных, с помощью которого можно создавать переменные, которые сочетают в себе несколько значений (например, координаты и имя). Внутри структуры каждому полю мы даем имя, чтобы потом обращаться к этому значению по его имени.

Для доступа к полям структуры используется точка:

Как видите, вы можете хранить в структуре столько полей, сколько вам угодно и они могут иметь разные типы.

Рассмотрим пример, демонстрирующий сочетание массивов и структур.

Так же как и с простыми типами (int, например), вы можете создавать массивы структур. А с каждым элементом этого массива работать так же как и с отдельной переменной. Для доступа к полю name первого элемента массива структур, просто напишите:

Структуры и функции

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

Функция getNewEnemy должна возвращать структуру с инициализированными полями:

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

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

Теперь эту переменную можно использовать как обычную структуру.

Передавать структуры в функцию можно так:

Когда мы передаем структуру в функцию, она копируется, так же как и при возвращении структуры. Поэтому любые изменения сделанные внутри функции будут потеряны, поэтому мы возвращаем структуру после изменения.

Когда вызывается функция, переменная ship копируется и изменяется в функции, а когда переменная возвращается, она снова копируется и перезаписывает поля оргинальной переменной.

И наконец, программа для создания и улучшения одного корабля:

Указатели

Если вы работаете с указателем на структуру, то для доступа к переменным надо использовать оператор «->» вместо точки. Все свойства указателей не изменяются. Пример:

Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих