Ответы пользователя по тегу C++
  • Qt(C++) на windows XP?

    @Mercury13
    Программист на «си с крестами» и не только
    Си++ и Qt сделаны кроссплатформенными, то есть скрывают разницу между платформами где-то у себя внутри.

    Компиляторы перестают поддерживать ту или иную ОС последними. Процессор-то остаётся тем же — ну и норма. Думаю, скомпилированное MinGW (без Qt) даже на 98 запустится.

    Так что разницы не будет, если весь наш софт будет работать на хост-ОС и все наши библиотеки будут запускаться на целевой ОС. А вот с поддержкой беда, версия 5.6 объявлена последней, которая официально поддерживает XP как хост-, и 5.7 — XP как целевую. Работает всё-таки? Вот и классно!

    Да. Раз уж в Qt есть три системы сборки — могут возникнуть вопросы, когда нужно редактировать руками pro-файл. Большинство руководств предполагают QMake. Я держусь на нём, потому что так и не выяснил, как QBS работает с многоядерными процессорами. Двести файлов в проекте — сами понимаете.
    Ответ написан
    Комментировать
  • Как передать counter is for loop внутрь макроса?

    @Mercury13
    Программист на «си с крестами» и не только
    Будет подставлено ИМЯ ПЕРЕМЕННОЙ b, а чему она равна — это уже другой вопрос. Никаких проблем не должно быть.
    #include <iostream>
    
    #define CHECK(a,b) \
       if ((a) == (b))     \
          std::cout << "Equal" << std::endl;  \
          else std::cout << "Inequal" << std::endl;
    
    int main()
    {
        for (int i = 0; i < 5; ++i) {
            CHECK (i, 2)
        }
        return 0;
    }


    После препроцессирования программа превратится вот во что…
    int main()
    {
        for (int i = 0; i < 5; ++i) {
            if ((i) == (2))
               std::cout << "Equal" << std::endl;
               else std::cout << "Inequal" << std::endl;
        }
        return 0;
    }


    Вывод в консоли
    Inequal
    Inequal
    Equal
    Inequal
    Inequal


    Разумеется, нехороши ситуации, когда в макрос подставляется код с побочными эффектами, но это другой вопрос.
    Ответ написан
    1 комментарий
  • Какие задачи решаются на C++?

    @Mercury13
    Программист на «си с крестами» и не только
    На Си++ пишутся сложные быстрые программы. Этим всё сказано.
    Игры, настольные программы, всякие там серверы…

    Лучше скажу, что НЕ пишется на Си++.
    1. Софт, сильно абстрагированный от машины, изменяемый пользователем и/или призванный запускаться где угодно: программы для iOS, Android, веба, высокоуровневые части некоторых игр…
    2. Компактные программы (например, для микроконтроллеров). Сейчас — вотчина Си и его уменьшенных библиотек.
    3. Простые программы, для которых быстродействие и установка дополнительного фреймворка — не проблема: 2D-игры наподобие Braid или Fez, графический редактор Paint.net…
    Ответ написан
    Комментировать
  • Реально ли 2d игра на С++ без граф.библиотек и движокв?

    @Mercury13
    Программист на «си с крестами» и не только
    Без DirectX или OpenGL никуды, это самые низкоуровневые API, дающие как скорость, так и мало-мальскую совместимость.
    Советую использовать тонкую обёртку над всем этим добром вроде SDL: думаю, интереснее будет писать игру, чем решать проблемы с Alt-Tab.
    А так, если задаться целью, можно небольшой движок сделать за неделю-две. Остальное лучше оставить на багофиксы и наполнение.
    Не советую работать с гексами, и вот почему. Гексы сразу же подразумевают, что игра пошаговая. ИИ замучитесь писать!

    И ещё. Систему анимации-то можно за это время написать, но сделать хороший редактор анимаций сложнее. Каждый кадр — отдельная картинка, и всё?
    Ответ написан
    1 комментарий
  • Нарушают ли указатели и разименование в c++ принципы ООП?

    @Mercury13
    Программист на «си с крестами» и не только
    Не будем встревать в холивар «должен ли указатель быть объектом». Примем, что указатель — простейший тип, ради совместимости и эффективности.
    К простейшему типу инкапсуляция и наследование неприменимы.
    А вот для полиморфизма указатели очень нужны. Динамический полиморфизм — это когда под одним фасадом могут оказаться разные объекты.
    1. По копии их передавать невозможно, только по указателю/ссылке.
    2. Их нельзя уничтожать под одну гребёнку. А значит, если мы их передаём в чьё-то другое владение, надо удостовериться, что они созданы в «куче» и у «фасада» есть виртуальный деструктор.

    Есть ещё два принципа ООП — абстракция и принцип Лисков. Первый имеет отношение к указателям постольку, поскольку есть полиморфизм. Второй — гугли «ковариантные/контравариантные указатели».
    Ответ написан
    1 комментарий
  • Как написать структура классов платформера?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Перед нами серьёзный проект: есть и сеть, и подкачка, и поиск путей. Если вы будете писать платформер с нуля, вероятно, «лишние» компоненты вы писать не будете.
    2. Это компоненты, а не классы. Структура классов в геймплее будет более густая, а в рендеринге и физике — менее.
    Например, пишем 2D-рендеринг. Я бы делал Renderer, Tileset, TileLayer, Sprite, Particle…

    В зависимости от желания и целей, лучше воспользуйтесь чужим движком или напишите свой простенький. Куча вопросов отпадёт.
    Ответ написан
    Комментировать
  • Можно ли обновлять модель в Qt по таймингу?

    @Mercury13
    Программист на «си с крестами» и не только
    Нет, так нельзя. И вообще, эти части модели вызывает элемент управления (например, QTreeView). Тем чаще, чем активнее мы работаем с данными.

    Как я понял, вы столкнулись с ситуацией: доступ к данным медленный. Значит, надо наладить кэш, чтобы rowCount, parent, index и прочие в большинстве случаев исполнялись мгновенно.

    Хорошо, задача номер два: где-то в дебрях второго потока идёт долгое обновление модели, а мы хотим её по частям отображать. Это дело сложное, и универсального ответа нет.

    Начнём с того, что синхронизировать поток и UI удобнее всего через сигналы-слоты-emit с типом соединения Qt::QueuedConnection (BlockedQueuedConnection, думаю, многовато будет). У слота будет один параметр: сколько элементов уже подгрузили.

    Для каждого узла, чьих сыновей мы будет подгружать, указываем дополнительное поле: сколько элементов видно из элемента управления. Все сложные структуры данных закрываем мьютексом. Поскольку дело это сложное, для тех объектов, чья ёмкость ограничена и невелика, лучше этого не делать — например, если у нас группы-альбомы-песни, можно делать это только для корневого элемента: даже у продуктивных групп вроде «битлов» элементов не так много.

    В слоте производим такую фишку: rowsAboutToBeInserted, изменяем наше дополнительное поле, rowsInserted. И так для всех элементов, у кого добавилось потомства.
    Ответ написан
    3 комментария
  • Как на 32-битной платформе в переменную типа intptr_t может влезть максимальный адрес ссылки?

    @Mercury13
    Программист на «си с крестами» и не только
    зачем нужно было вообще вводить intptr_t если есть uintptr_t, в который адрес точно влезет?

    intptr_t — разность двух адресов.

    И, более того, как в ЗНАКОВУЮ переменную типа intptr_t можно поместить 32-битный адрес памяти, если в этом числе 1 бит уходит на знак, а для данных остаётся 31 бит?

    Учите матчасть — как действует дополнительный код, почему машинные целые изображают в виде круга и почему знаковое и беззнаковое сложение выполняется одними и теми же операциями add/sub. В общем, данные записываются во все 32 бита. И в знаковый тоже.

    как может БЕЗЗНАКОВОЕ число равняться числу СО ЗНАКОМ?

    А вот сравнивать их — ошибка, и не зря большинство компиляторов выводит предупреждение. Оба надо перевести либо в unsigned, либо в signed, либо в более крупный знаковый целый тип.
    Ответ написан
    2 комментария
  • Причина работы данного массива структур?

    @Mercury13
    Программист на «си с крестами» и не только
    Таков смысл «си с крестами» — «вы не платите за то, чем не пользуетесь». В данном случае: не пользуетесь авариями — не платите за них. Кстати, самый эффективный способ обработки аварий на x86 только недавно лишился патента, и я не в курсе, есть ли он в MinGW (на x64 патент обошли и он там давно был). Плюс совместимость с Си.

    Правда, мне пришлось сделать свои массивы Array1d и Fix1d именно для того, чтобы проверять границы: включено в debug и выключено в release.
    Ответ написан
    Комментировать
  • Почему в C++ нужно строить всю программу на ООП (длинный вопрос)?

    @Mercury13
    Программист на «си с крестами» и не только
    Задача ООП: 1) Локализовать изменения состояния объекта (инкапсуляция); 2) связывать разные кирпичики данных через стандартные интерфейсы (полиморфизм).

    Простейший тетрис не слишком велик, чтобы его писать на чистом ООП.
    Но представьте себе, мы начинаем налаживать настраиваемое управление джойстиком или клавиатурой. И тогда у нас появляется такой код.
    enum {
      BT_LEFT = 1,
      BT_RIGHT = 2,
      BT_ROTATE = 4,
      BT_SOFTDROP = 8,
      BT_HARDDROP = 16,
      BT_PAUSE = 32,
      BT_CONNECTED = 32768,   // бит, указывающий, что контроллер подключён
    };
    class Controller {  // интерфейс
    public:
      virtual unsigned poll() const = 0;   // сочетание битов BT_XXX
      virtual ~Controller = default;
    };

    Классы Keyboard и Joystick поддерживают интерфейс Controller, и подмена клавиатуры на джойстик и наоборот ничего не изменит.
    Вот вам полиморфизм.

    Текстовый редактор превращаем в многооконный — берём класс Editor и пристраиваем его не к программе в целом, а к MDI-окошку. Вот вам инкапсуляция — локализованное изменение состояния.

    Я как-то мучил движок Doom. Он написан в самом настоящем объектном стиле на чистом Си! Хотя и там были проблемы: сетевой код был куда хуже по качеству, чем сам движок. Писали разделённый экран, глобальную переменную netgame разделили на две, multiplayer и netgame и долго-долго правили баги, где multiplayer, где netgame (было дело, участник десматча ввёл IDKFA, это сработало и вызвало рассинхронизацию). А код пользовательского интерфейса — вообще медвежуть!
    Ответ написан
    Комментировать
  • А как реализуют всякие разархировщики файлов игр для модификаций?

    @Mercury13
    Программист на «си с крестами» и не только
    Сам я бросил ковырять игры давным-давно, и мои знания устарели. Но вот в старых играх, где упаковки ещё не было, я успешно распознавал кое-какие форматы. Но в целом задача состоит из двух: опознание формата архива и опознание формата файла в архиве.

    Архив, как правило, устроен просто (хотя разработчики могут зашифровать его, как в «Мафии»). Разжатие чаще всего делается каким-нибудь из стандартных алгоритмов типа Deflate или LZMA. В принципе, можно и игру дизассемблировать, если алгоритм необычный, но чаще всего нет нужды. Кстати, разжатие из-за того, что оно критично к скорости, не покрывают всякими там Denuvo.

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

    Для утилит используют любой доступный язык высокого уровня, чаще всего Си++.
    Ответ написан
    Комментировать
  • Как пишутся библиотеки для C++ и других ЯП?

    @Mercury13
    Программист на «си с крестами» и не только
    В 99,9% случаев — на языках высокого уровня. Как слой более низкого уровня используются три вещи.
    1. Стандартная библиотека языка.
    2. Внутреннее API ОС (WinAPI/POSIX/DOS).
    3. Чужие низкоуровневые библиотеки.

    Есть такое понятие «полнота по Тьюрингу» — грубо говоря, это значит, что язык способен решать те же задачи, что и машина Тьюринга (простое гипотетическое программируемое устройство). Все языки программирования полны по Тьюрингу, то есть ими можно закодировать любой алгоритм, зачем ассемблер?
    Ассемблер используется ограниченно во внутренних циклах, где важна скорость.

    Здесь под «низким уровнем» понимается «мало автоматизации», «очень тонкая прослойка между API операционной системы и нашим новым интерфейсом», «минимум управления памятью» — то есть функция, например, принимает не std::string, а указатель на нуль-терминированную строку const char*. И для написания библиотек любят языки, способные работать на низком уровне — в первую очередь Си, Си++ и Паскаль. Если нет ограничения по скорости, ассемблер не обязателен.
    Ответ написан
    1 комментарий
  • Как открыть порт на роутере? Комплексное решение?

    @Mercury13
    Программист на «си с крестами» и не только
    1. https://portforward.com/tp-link/tl-wr740n/
    www.pcwintech.com/port-forwarding-tp-link-tl-wr740...
    forum.tp-linkru.ru/viewtopic.php?t=9
    Но не забывайте, что это требует статического IP-адреса сервера, и лучше всего это сделать, прописав статическую зависимость MAC-IP. По третьей сцылке это есть.
    2. Гуглите UPnP. Посмотрите, допустим, вот. Использование UPnP C++
    3. Сам даже не знаю. Как-то само из кусочков понимание собралось. Правда, я в своё время налаживал локальные «сети» по COM- и LPT-кабелю. А потом по-чёрному резался в разные игры по интернету. Разумеется, тонкостей настройки больших сетей мне это не дало, но как ходят пакеты, примерно понимаю.
    Ответ написан
    2 комментария
  • При завершении работы программы падает исключение, что делать?

    @Mercury13
    Программист на «си с крестами» и не только
    Это испорченная память, где-то ошибка в управлении памятью (например, запись за границами массива).
    UPD. Ваша ошибка: в составе std::string есть внутренние неконтролируемые поля, и его нельзя побайтово сохранять в файл. Такими неконтролируемыми полями могут быть указатели, кэши-ускорители и многое другое. Вообще std::string состоит из указателя или двух, и отсюда следуют две вещи. 1) При сохранении в файл не попадут строковые данные, попадёт только указатель. К тому же в Windows нет хорошего 16-ричного просмотрщика, а без него при работе с файлами как без рук (по крайней мере начинающему, я давно обхожусь). 2) Как только вы этот указатель загрузите, std::string портится, и на деструкторе может случиться что угодно.

    Вам надо самим придумать формат файла и реализовать загрузку/сохранение, используя length(), data() и front().

    Важное правило: ткни в любой байт дампа вашего файла — вы должны сказать, что он значит. Из-за сложности формата это может быть сложно. Байт может быть неиспользуемым или оставленным для совместимости, но какое-то значение должно быть. Если формат многоуровневый, надо сказать назначение на всех уровнях: например, «это значение атрибута XML» на уровне XML и «это имя студента» на прикладном уровне.

    Исключение: если есть объект-«чёрный ящик», который написан не нами и сериализуется гарантированно корректно, достаточно сказать: «это часть чёрного ящика». (Сериализуется — это переводится в цепочку байтов, например, файл или сетевой поток.)

    Что ещё пока вижу (но это не причина ошибки).
    1.
    unsigned short int *exam_t = new unsigned short int[5];
    Нет нужды new, невелик массив. Хватает локального массива на стеке. Аналогично остальные два new.
    2. Нет нужды давать clear/close. Это фишка Си++, автоматически сработает деструктор.
    3. Не называйте переменную flag, называйте wasFound (или что она реально значит).
    4. sizeof(&student_r) только чудом совпадает с sizeof(student_r).
    5. Не надо писать flag == false, надо !flag.
    6. Функция del_reversive делает излишнюю работу и переставляет студентов, к тому же есть стандартный алгоритм remove_if.
    7. while (!flag) в del_rev бессмысленно.
    8. Вы же работаете со string’ами — зачем вводить информацию в буфер ограниченной длины?
    9. Программа не модульная, нет нужды в хедере.
    Ответ написан
    Комментировать
  • Существуют ли правила подключения include-файлов в С++?

    @Mercury13
    Программист на «си с крестами» и не только
    Незыблемых правил два.
    1. В CPP первым — свой хедер. Это даёт уверенность, что в хедере нет недостающих включений.
    2. Избегать циклической зависимости по интерфейсам. Делить программу на слои и не включать ничего из верхних слоёв.

    Если уж второе не получилось — есть три решения, каждое из которых решает свою задачу.
    1. Forward declaration, полумера против сильной связанности классов.
    // Хедер 1
    class Master;
    
    class Slave {
      Master* master;
    }
    
    // Хедер 2
    class Master {
      Slave slave;
    }


    2. Разорвать порочный круг интерфейсом.
    // Хедер 1
    class Master {  // interface
    public:
      virtual void enumSlaves(const EnumSlavesCallback& v) = 0;
      virtual ~Master() = default; 
    }
    
    class Slave {
      Master* master;
    }
    
    // Хедер 2
    class MyMaster :  public Master {
    public:
      Slave slave;
      void enumSlaves(const EnumSlavesCallback& v) override {}
    }


    3. Вынести общие типы/функции в отдельный хедер под названием someDefines.h или someUtils.h.
    Ответ написан
    Комментировать
  • Как можно решить эту задачу логически иначе?

    @Mercury13
    Программист на «си с крестами» и не только
    В лоб.
    Если a > b, обменять a и b.
    Если a > c, обменять a и c.
    Если b > c, обменять b и c.
    Меньшим количеством обменов это сделать невозможно: два обмена дают до 4 вариантов расстановки, а 3!=6.
    Ответ написан
    Комментировать
  • Как правильно организовать код большого проекта на C++(и не только)?

    @Mercury13
    Программист на «си с крестами» и не только
    Имена классов с большой буквы в CamelCase, например class EventHandler;.

    Принято в Java, Qt.

    Названия объектов аналогично, но первая буква маленькая, например Image backgroundImage;.

    Принято в Java, Qt.

    Названия примитивных типов(int, char, double, etc) маленькими через нижнее подчёркивание, например double percent_of_fails;

    Думайте как хотите, но, по-моему, нет нужды.

    Названия методов с мальнькой в camelCase и отражают действие функции, например void addModule(std::shared_ptr module);

    Принято в Java, Qt.

    Структура файлов:

    При таком количестве файлов (и даже впятеро большем) — норма.

    Первым идёт инклуд C++ хедеров, затем сторонние библиотеки типа буста, затем *.i файл из данной директории и после всё остальное.

    Свой хедер → стандартные библиотеки Си/Си++ → сторонние библиотеки, причём чем больше мы от них архитектурно зависим, тем они раньше → внутренние файлы (опять-таки, движок раньше утилит и наборов данных).

    Первым свой хедер — это архиважно. Как правило, модули идут парой «хедер + единица компиляции», и мы сразу же убеждаемся, что в хедере есть все нужные #include.

    1 forward declarations(чтоб не замусоривать ими остальные файлы)

    Forward declarations конкретно чего?

    2 инклуды всех *.h файлов из данной директории.

    Пункт спорный даже не из-за перекомпиляции, а из-за 1) циклической зависимости между модулями; 2) если сделать это в хедере — может привести к некомпилирующемуся проекту.

    Сущности в каждой директории имеют своё одноимённое пространство имён,

    Пространств имён должно быть намного меньше, чем файлов, плюс они должны быть предельно короткими. Избегать using namespace.

    Кроме того…
    1) Рекомендую в каталоги поиска хедеров поставить основной каталог проекта. В #include ни в конем случае не должно быть «каталог вверх» (..).
    2) Возможно, чужие библиотеки стоит вынести из каталога проекта. Их каталоги также стоят в каталогах поиска хедеров.
    3) Категорически запрещены повторы файлов в разных каталогах.
    Ответ написан
    2 комментария
  • WinAPI: Нарушение прав доступа при записи. Как исправить?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Ошибка в вызове ReadFile. Причём я говорил про эту ошибку.
    DWORD nRead;
     isSucceed = ReadFile(myfile, buffer, 10, &nRead, NULL);


    А также.
    2. Файл открывать только для чтения.
    3. Проверить myFile на корректность.
    4. Читаем 10 байтов, пишем 100?
    Ответ написан
    Комментировать
  • Как вывести результат работы функций WinAPI?

    @Mercury13
    Программист на «си с крестами» и не только
    Ваши ошибки.

    1. Это разные дескрипторы.
    FindFirstFile даёт дескриптор поиска файлов.
    А ReadFile требует дескриптор файла, который можно получить через CreateFile.

    2. Второй параметр FindFirstFile не может быть NULL. Это указатель на WIN32_FILE_DATA, в которую функция и будет кидать информацию о найденных файлах.

    3. Если вы не ищете по маске и точно хотите открыть файл, на что вам FindFirstFile? Орудуйте сразу CreateFile.

    4. Вы подключили пару излишних хедеров.

    5. Не советую вместо нулевого указателя NULL писать 0.

    6. В ReadFile два последних параметра не могут одновременно быть NULL: первый — для синхронного чтения, второй — для асинхронного.

    В общем, «курите маны». Благо, у M$ они хорошие.
    Ответ написан
    9 комментариев
  • Как перевести код из С++ в Си?

    @Mercury13
    Программист на «си с крестами» и не только
    Перед нами совершенно необъектный код, на вид тупо написанный на Си средствами Си++. Так что будет несложно.
    1. Убрать из структуры private/public.
    2. Вынести функции из структуры, явно прописав в них this:
    int BMP::Load(char FileNames[])
    →
    int BMPLoad(struct BMP* this, char FileNames[])

    3. Для старых версий Си — поднять определения всех переменных на верх функции.

    А что нам с масштабированием? — на вид написан на том же Си и ничего не требует, кроме задания этого this.
    Ответ написан
    3 комментария