Ответы пользователя по тегу C++
  • Как обрабатывать события в Qt?

    @Mercury13
    Программист на «си с крестами» и не только
    Вы, разумеется, орудуете встроенным в Qt Creator редактором форм…
    1. Визуально: ПКМ, Go to slot…
    Программно: функцией connect, это описал AtomKrieg.
    2. Все компоненты находятся в доп. объекте ui. Когда вы делаете форму программно, им не обязательно там быть, но редактор форм делает именно так.
    if (ui->textEdit->text() == "Hi") label->setText("Hi!");
    Ответ написан
  • Как задать програмнно отобразить поверх других форм форму вызванную через Show()?

    @Mercury13
    Программист на «си с крестами» и не только
    Комментировать
  • Передача данных по ftp. Как сделать?

    @Mercury13
    Программист на «си с крестами» и не только
    Наиболее известная штука — cURL. Помимо FTP, поддерживает FTP+TLS и SFTP.
    Вот пример для FTP-закачки.
    https://curl.haxx.se/libcurl/c/ftpupload.html

    На C++ лучше использовать «плюсовые» обёртки для cURL, но код в целом такой же.
    Ответ написан
    Комментировать
  • Как рисовать на QPixmap?

    @Mercury13
    Программист на «си с крестами» и не только
    Ваши ошибки.
    1. QPixmap — сам по себе указатель совместного владения; если надо его ненадолго создать — делайте на стеке, никаких new!
    1.1. Аналогично и QPainter, только он указатель единоличного владения. Кстати, это однажды сыграло с одной моей программой злую шутку, когда она выжрала всю память.
    2. Зачем нужна генерация Pixmap’а в процедуре перерисовки формы? Если ваш потомок сам корректно рисует картинку — генерируйте её по событию «что-то изменилось».

    Далее, если картинок может быть конечное, но достаточно большое число (например, тысячи), можно наладить кэш, где хранятся подготовленными, например, последние двадцать. Если картинок немного (до сотни), лучше при загрузке программы подготовить их все.
    Ответ написан
    Комментировать
  • Почему возникает ошибка LNK2001 Неразрешенный внешний символ символ?

    @Mercury13
    Программист на «си с крестами» и не только
    Как говорят, «или крест снимите, или трусы наденьте». И учите понятие «единица компиляции».
    По какой схеме устроен ваш проект? «Одна единица компиляции» или «много единиц компиляции»?

    Си недалеко ушёл от ассемблеров. А в ассемблерах программа компилировалась по частям и собиралась воедино линкером (компоновщиком, редактором связей) — в те времена кода было много, а данных мало. Многие из ошибок невозможно было определить, не запустив линкер. Си++ пользуется многими из архитектурных особенностей ассемблеров и Си — по крайней мере ни одно из модульных решений не стало рекомендацией (кроме костыля extern template class).

    Но как говорить «переменная/функция есть, такого-то типа и в другой единице компиляции»? Для этого есть прототипы функций и extern-определения переменных. Их обычно вносят в заголовочные файлы с таким требованием: ничего, что находится в заголовочном файле, не должно производить кода. А код производят…
    • Глобальные переменные (без typedef, extern).
    • Нешаблонные функции (кроме inline).
    • Полностью специализированные шаблонные функции (кроме inline).
    • Команда «специализировать шаблон» (template class).

    При этом…
    • Функции в теле struct/class автоматически inline и кода не производят.
    • Для неявной специализации шаблонов существуют обходы — код генерируется дважды, но ошибки не выдаёт.
    • «Свой» хедер обычно включают первым, чтобы убедиться, что в нём нет недостающих зависимостей.

    В системе «одна единица компиляции» всё просто: есть ровно один файл, подлежащий компиляции. Тогда в хедерных файлах вполне могут быть конструкции, производящие код.

    Системы «одна единица компиляции» и «много единиц компиляции» можно комбинировать, но надо знать:
    • Все хедеры, которые производят код, должны подключаться из одной-единственной единицы компиляции. Надо чётко осознавать, из какой, и не подключать из чужих.
    • У библиотеки всё равно должен быть хедер-фасад, не производящий кода и предназначенный для стыковки с другими единицами компиляции.
    Такая конструкция ускоряет полную перекомпиляцию и часто применяется для библиотек, но надо знать: огромные библиотеки вроде SqLite, в 5 мегабайт препроцессированного кода, мешают распараллеливанию компиляции (ибо пока откомпилируется SqLite, остальные процессоры вполне себе соберут остальную программу).

    // В схеме «одна единица компиляции»: ничего не делать.
    // В схеме «много единиц компиляции»: лишняя зависимость; унести в unit1.cpp
    #include <cstdlib>
    
    // В схеме «одна единица компиляции»: убрать extern.
    // В схеме «много единиц компиляции»: завести unit1.cpp, там сделать MyType x;
    extern MyType X;
    
    // В схеме «одна единица компиляции»: ничего не делать.
    // В схеме «много единиц компиляции»: перенести функцию в unit1.cpp, оставив в .h только прототип.
    void XReset() {}
    Ответ написан
    Комментировать
  • Как написать SMTP-клиент, чтобы не попасть в список спамеров?

    @Mercury13
    Программист на «си с крестами» и не только
    Да уж, злой там спамодетектор…
    Написать сообщение реальным почтовиком и повторить как можно больше полей из этого самого почтовика.
    Пожалуй, главное — User-Agent. Процентов 80 ботофильтров ссылаются именно на него.
    Ответ написан
    Комментировать
  • Как сделать двухстрочную шапку в таблице в С++ Builder?

    @Mercury13
    Программист на «си с крестами» и не только
    Нельзя, я использовал компонент KGrid.
    Ответ написан
    Комментировать
  • Как определить метод класса, чтобы объект в него передавался не по ссылке?

    @Mercury13
    Программист на «си с крестами» и не только
    UPD. Теперь понял, о чём вы. В таком виде нельзя.
    Ответ написан
  • Почему a=1, i=1, j=2?

    @Mercury13
    Программист на «си с крестами» и не только
    Всё логично. Операция || даёт единицу, если хотя бы один параметр не 0, и ноль — если оба нули.
    К тому же операция || «экономная». Если первый аргумент не ноль — результат автоматически 1.

    i = 1
    1 || ? = 1,
    j++ не вычисляется вообще.
    Ответ написан
    Комментировать
  • Как заполнить динамически созданный массив?

    @Mercury13
    Программист на «си с крестами» и не только
    Кода нет даже игрушечного, и гарантированно не скажешь. Я вижу три возможных причины.
    1. Либо не прописан конструктор копирования или операции присваивания (а с деструктором всё в порядке). Указатели продолжают обращаться к освобождённой памяти, и по какому-то счастливому совпадению первый элемент совпадает.
    2. Либо конфликт имён: fn — параметр конструктора и fn — внутреннее поле.
    3. У вас тут закомментированное srand(time(NULL));. Я не знаю, что в функции random_at_most, но если там srand — перенеси его в другое место. Процессор работает в миллион раз быстрее, чем таймер. «Хочешь, чтобы было случайнее», а будут повторы.
    Ответ написан
    Комментировать
  • GetModuleFileNameA, в чем проблема?

    @Mercury13
    Программист на «си с крестами» и не только
    sz = strlen(szPath);
    Ну чему, блин, равняется sz?


    Return value

    If the function succeeds, the return value is the length of the string that is copied to the buffer, in characters, not including the terminating null character. If the buffer is too small to hold the module name, the string is truncated to nSize characters including the terminating null character, the function returns nSize, and the function sets the last error to ERROR_INSUFFICIENT_BUFFER.

    Вот оттуда надо брать длину. А не из strlen неинициализированной памяти.

    И идеалом было бы заложиться на пути длиннее MAX_PATH.
    Ответ написан
    Комментировать
  • Как работать со строковыми матрицами?

    @Mercury13
    Программист на «си с крестами» и не только
    Простейший способ — отсортировать каждую строку обеих матриц. Строки, изначально являвшиеся анаграммами, будут совпадать, и теперь уже можно выяснять, что с чем совпадает. Сложность O(w·(hA+hB)·max{hA+hB, log w})

    Более сложный (лучше асимптотическая оценка). После того, как отсортировали каждую строку, строки матрицы B дополнительно переупорядочиваем в лексикографическом порядке (ААА < ААБ <…< ААЯ < АБА). Для поиска совпадений пользуемся двоичным поиском. Сложность O(w·(hA+hB)·log max{w, hA, hB}).

    Можно также каждой строчке матрицы B посчитать хэши и запомнить в какой-то структуре данных: хэш не совпал ни с чем — полное сравнение не проводится. Не улучшает оценки в худшем случае, но улучшает в среднем.

    Офтоп. Было дело, я оптимизировал WAD’ы Doom’а по размеру (формат позволял нескольким блокам ссылаться на один и тот же участок файла, а во многих модах — и даже в самом Doom’е — были повторы). Обошёлся хэшированием, и хэшем был размер + несколько байт из середины блока.
    Ответ написан
    Комментировать
  • Как правильно унаследовать виджет QT?

    @Mercury13
    Программист на «си с крестами» и не только
    Наследоваться-то надо от QListWidget…
    Ответ написан
    2 комментария
  • C++ удаления дубликатов, почему не работает?

    @Mercury13
    Программист на «си с крестами» и не только
    void Wid::test() {
        vector<string> stp;
        stp.push_back(text->toPlainText().toStdString());
        // В stp одна штука
        sort(stp.begin(),stp.end());
        // Ну что ей будет, этой штуке?
        stp.erase(unique(stp.begin(),stp.end()),stp.end());
        // unique даст end; поведение vector при этом не определено.
        ofstream filesave(pp);
        copy(stp.begin(),stp.end(), ostream_iterator<string>(filesave,"\n"));
    }
    Ответ написан
    3 комментария
  • C++QT5: UTF-8, QString, QByteArray, *char и русские символы. Как изменить элемент в массиве *char?

    @Mercury13
    Программист на «си с крестами» и не только
    В кодировке UTF-8 один символ — это от одного до четырёх байт. Смиритесь, и если хотите менять русскую букву (2 байта) на английскую (1 байт), лучше работать в кодировке UTF-16.
    word1 = word;
    word1[0] = 'X';

    или
    word1 = QString::fromUtf8(ar.data());
    word1[0] = 'X';

    А если нет возможности — то разбирать UTF-8, разумеется.

    Вообще многобайтовые кодировки неудобны для подобных операций.
    Ответ написан
    Комментировать
  • Какой способ организации чтения и записи файлов в разных форматах оптимальнее?

    @Mercury13
    Программист на «си с крестами» и не только
    Портяночный способ хорош, когда на выходе — файл простой структуры. Например, стандартные файлы межпрограммного обмена наподобие HTML, PNG…
    Рекурсивный удобнее для внутреннего формата хранения, когда надо в подходящем виде сериализовать всё, что в программе есть, да ещё и наладить поддержку старых версий этой сериализации.
    Ответ написан
    Комментировать
  • Какими приёмами вы пользуетесь чтобы различать указатели, которые нужно освобождать, от указателей, которыми нужно только пользоваться?

    @Mercury13
    Программист на «си с крестами» и не только
    Но есть ещё один указатель, который реализует именно что единую ответственность — std::unique_ptr. Также я использую одну самоделку — но, возможно, «не умею готовить» unique_ptr.
    Ответ написан
    1 комментарий
  • Какие обьекты удалять в деструкторе?

    @Mercury13
    Программист на «си с крестами» и не только
    После того, как отработает тело деструктора, для всех полей объекта автоматически вызываются деструкторы, в обратном порядке.
    У int и double деструкторы нулевые — но другие-то и не нужны.

    В каких случаях надо писать деструктор самим…
    1. Мы владеем каким-то ресурсом, но штатный деструктор его не уничтожает.
    • Простой (не умный) указатель и выделенная память — это хорошо описал sitev_ru .
    • Объект-блокировщик, например, мьютекса (мьютекс — примитив межпоточной синхронизации, не дающий зайти в определённые участки кода одновременно двум потокам).
    class Mutex {
    public:
      void enter();
      void leave();
    }
    
    class Lock {
    public:
      Lock(Mutex& aMutex) : mutex(aMutex) { mutex.enter(); }
      ~Lock() { mutex.leave(); }
    private:
      Mutex& mutex;
    }
    
    …
    Mutex mutex;
    { Lock lock(mutex);
      // всё, что здесь, выполняется внутри мьютекса.
      // Даже если выпадет авария, из мьютекса корректно выйдем.
    }

    2. Сложный порядок уничтожения. Односвязный список из std::unique_ptr будет работать и так, со штатным деструктором, но это чревато переполнением стека.
    3. Сложная структура владения, и при уничтожении надо автоматически отобрать объект у владельцев. По-чёрному используется в оконных фреймворках на манер VCL и Qt. Удаляем компонент — он автоматически отбирается у владельца.

    По опыту: если структуры данных выносить в отдельный объект (ну и использовать STL, где можно), 80% объектов Си++ будут с автоматическим деструктором.

    ЗЫ. По результатам прикидочных подсчётов в живом проекте, ≈150 файлов (реально файлов 219, но не все наши собственные; библиотечные деструкторы не учитывал).
    • Классов с настоящим деструктором — около 30. Большей частью системные (W32Cs — быстрый мьютекс Win32) или структуры памяти (Array1d, например). Из них в собственно проекте (не в личной библиотеке программиста) — три (!): один связан с автовладением чужой библиотекой XLSX, два — с фоновыми потоками.
    • Интерфейсов с пустым виртуальным деструктором — порядка 40.
    • И ещё деструкторы, автоматически добавленные Qt — по числу форм, ровно 20.
    • И единицы пустых деструкторов, добавленных по желанию левой пятки линкера.
    Ответ написан
    Комментировать
  • Как подсчитать комбинацию шагов коня на матрице 4 на 3?

    @Mercury13
    Программист на «си с крестами» и не только
    Твоё дело — перебор с кэшированием.
    Для каждой кнопки вручную закидываем в массив конских «соседей» — ни одного для 5-ки, три для 4 и 6, два для остальных.
    Затем заведи массив 10×7 (стартовая кнопка×длина) и устрой рекурсию с одним небольшим добавлением: если оно закэшировано, брать из кэша. Правила — f(b, 1) = 1, для остальных — sum{c=сосед(b)} f(c, i−1).
    Можно и динамическим программированием, без рекурсии — всё равно расход вычислительной мощи незначительный. Сначала f(b, 2), затем f(b, 3), и т.д. до 7.
    Ответ написан
  • Освобождается ли память по завершению выполнения функции, если массив инициализировать внутри функции?

    @Mercury13
    Программист на «си с крестами» и не только
    char * a = "hello!";
    Для указателя a — разумеется, на стеке.
    Строка, на которую он смотрит, "hello",— строковый литерал. Для него память изначально, при пуске программы выделена в сегменте данных. Освобождать не надо. На многих платформах — хоть на это рассчитывать не надо — этот сегмент защищается от записи и при попытке туда записать AV.
    Ответ написан
    8 комментариев