Ответы пользователя по тегу C++
  • Undefined reference to?

    @Mercury13
    Программист на «си с крестами» и не только
    В проект не включили файл methods.cpp. Потому линкер говорит, что нет этих функций.

    Язык Си (как, впрочем, и большинство ассемблеров) собирает cpp-файлы воедино неязыковыми средствами: makefile’ами и файлами проектов. Сначала компилятор обрабатывает все единицы компиляции отдельно друг от друга, а потом линкер собирает из того, что получилось, исполняемый файл. Разумеется, если где-то какой-то функции не нашлось, это можно опознать только при линковке.
    Ответ написан
    4 комментария
  • Почему при достаточно малых значениях вещественного числа кол-во итераций при нахождении машинного эпсилон резко убывает?

    @Mercury13
    Программист на «си с крестами» и не только
    Машинный эпсилон — это минимальное число, что 1 + ε ≠ 1. Так что, в принципе, ты вычислил его правильно, хоть код и студенческий. Но есть один нюанс.

    Дело в том, что float и double бывают нормализованные и денормализованные. Что это такое?
    Любое число в двоичной системе счисления начинается с единицы. Поэтому головная единица подразумевается и не хранится — т.н. «нормализованное число». НО: когда порядок 00…00, считается, что в голове НОЛЬ, а относительная погрешность сменяется абсолютной — это денормализованное число.

    0 1010…00 00000001 = +0,11012×2−127 — нормализованное число
    0 1010…00 00000000 = +0,01012×2−127 — денормализованное
    0 0000…00 00000000 = +0,00002×2−127. Ноль — тоже денормализованное число.

    10−38 — минимальное нормализованное число. 10−45 — минимальное денормализованное, с мантиссой 0,00…001. Помнишь, я говорил, что в денормализованных числах относительная погрешность сменяется абсолютной в эти самые 10−45 — потому чем меньше число, тем больше «типа-эпсилон».

    10-байтовый extended, он же long double, насколько мне известно, не нормализован, т.е. головная единица там хранится явно. Но такая точность редко нужна, появляется перерасход памяти (2 или 6 байтов, в зависимости от процессора и его настроек), да и не слишком оптимизируют сопроцессоры под такие числа.
    Ответ написан
    Комментировать
  • Использование функций из Dll бибилеотеки?

    @Mercury13
    Программист на «си с крестами» и не только
    Возникает вопрос. Это простой DLL с торчащими наружу функциями или COM?
    • QLibrary — для DLL.
    • regsvr32 — это для COM.
    • Первый раз не загрузилось и второй раз загрузилось, потому что 32-битные программы со стандартным манифестом принимают каталог «SysWOW64» за «System32». Когда DLL не найден, программа отыскивает его в некоторых стандартных местах и таковыми, возможно, являются и активные COM-серверы.
    • OpenConnection мог не найтись по адовой куче причин. Может оказаться, что имя OpenConnection «покозявлено» и надо что-то типа «OpenConnection@4». Может оказаться, что библиотека — действительно COM-сервер и QLibrary тут бесполезен.
    Ответ написан
    9 комментариев
  • Почему не отрабатывает перегруженный конструктор класса?

    @Mercury13
    Программист на «си с крестами» и не только
    Попробую рассказать об этом же более простыми словами.

    Текст String s3 = "Нас обманули, расходимся."; означает вот что. Мы создаёт временную строку «Нас обманули, расходимся», а затем присваиваем её нашему s3. Конечно, компилятор это потом заоптимизирует, но это семантика языка, и ей надо следовать.

    Есть два способа передать временный объект в функцию. Любую: хоть простую, хоть конструктор, хоть операцию =.
    Первый — константная ссылка: String(const String &S).
    Второй способ из C++11 — временная ссылка: String(String &&S).

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

    class String
    {
    public:
        String() {}
        String& operator= (String& s) { return *this; }
    };
    String operator+ (String a, String b) { return String(); }
    
    int main()
    {
        String s1;
        String s2 = s1;
        String s4;
        s4 = s1 + s2;
    }


    Решается String& operator= (const String& s).
    Ответ написан
    Комментировать
  • Как хранить записи со стены ВК?

    @Mercury13
    Программист на «си с крестами» и не только
    Не громоздко и не ресурсоёмко (особенно в C++11 — кстати, скоро Qt вообще откажется от 03).
    Однако я бы в таких нагромождениях делал struct’ы из одного элемента. Что-то типа
    struct Comment {
      QMap<QString, QString> objects;
    };
    
    struct Post {
       QList<Comment> comments;
    };

    Так код проще будет разобрать.

    Только одно непонятно. Что собой представляют эти objects и на что тебе эта «строкотипизированность»?
    Ответ написан
    Комментировать
  • Как конвертировать list в string[]?

    @Mercury13
    Программист на «си с крестами» и не только
    Итератор — это объект с семантикой указателя, который может указывать на N+1 точку в объекте.
    5f40aef227644b9ca57224ffeb571a97.png
    Раз он с семантикой указателя, у него есть операции «унарная звезда» и −> (разыменование и разыменование+взятие поля). Также у итератора есть операция ++ (сдвинуться на следующую позицию). Если это т.н. «однонаправленный итератор» — всё, больше ничего.

    Также бывают т.н. двунаправленные итераторы (есть операция −−), и итераторы произвольного доступа (их можно свободно складывать с числами — ну совсем как указатели). В частности, у std::list итераторы двунаправленные.

    У итераторов неопределённое поведение…
    • при попытке выйти за начало или конец;
    • при попытке разыменовать, если он смотрит на последнюю позицию (отмеченную как «конец»).

    Конкретно о задаче.
    1. std::vector предпочтительнее std::list.
    2. Не нужно возвращать string*, хватает какого-нибудь контейнера (std::vector<std::string> или std::list<std::string>).
    3. Если функциональности и скорости istringstream хватает, флаг в руки! Я бы написал по хардкору, с нуля. Вот мой код, выдранный из моего проекта, наверно, будет несложно переделать его в учебный.

    void parseCommaList(
            const char *aStart,   // указатель на начало
            const char *aEnd,    // указатель на символ за концом
            char aComma,        // символ-разделитель
            bool aSkipEmpty,   // true, если пустые подстроки пропускать
            ProcParseCommaList aCallback,   // функция-нагрузка
            void *aData)   // этот параметр нужен, чтобы передавать какие хочешь данные в функцию-нагрузку, удаляй его смело!
    {
        str::trim(aStart, aEnd);    // моя функция; пододвигает aStart вперёд и aEnd назад, убирая пробелы.
                                    // Если удаление пробелов не нужно — удаляй! Если нужно — пиши сам.
        if (aStart == aEnd) return;
        const char *sstart = aStart;
        for (const char *p = aStart; p != aEnd; ++p)
        {
            if (*p != aComma) continue;
            const char *send = p;
            str::trim(sstart, send);   // то же самое, можно убрать
            if (p != sstart || !aSkipEmpty)
                aCallback(sstart, send, aData);    // замени на боевую нагрузку
            sstart = p + 1;
        }
        str::trim(sstart, aEnd);   // то же самое, можно убрать
        if (sstart != aEnd || !aSkipEmpty)
            aCallback(sstart, aEnd, aData);    // замени на боевую нагрузку
    }


    И, соответственно, версия для std::string.

    inline void parseCommaList(
            const std::string &aIn,
            char aComma,
            bool aSkipEmpty,
            ProcParseCommaList aCallback,
            void *aData)
    {
        parseCommaList(aIn.data(), aIn.data() + aIn.length(), aComma, aSkipEmpty,
                aCallback, aData);
    }
    Ответ написан
    Комментировать
  • Как добавить все элементы из вектора в контейнер multimap под определенный ключ?

    @Mercury13
    Программист на «си с крестами» и не только
    Если вам нужен именно multimap, другого способа я не вижу. Но если элементов много, стоит задействовать хинт (параметр position, где примерно элемент будет стоять). Что-то типа (в компиляторе не проверял, код почти гарантированно неверный).
    if (!names.empty()) {
        Users::const_iterator hint = users.upper_bound(1);
        for (auto &i :  names) {
          users.insert(hint, {1, i});
        }
    }

    Учтите, что при переходе с 03 в 11 поменялся смысл хинта, тут версия для 11.
    P.S. Извините, код для хинта можно сделать и проще. После вставки и ++ хинт останется тем же.
    Ответ написан
    Комментировать
  • Как корректно обрезать строку тип string в utf8 на с++?

    @Mercury13
    Программист на «си с крестами» и не только
    Символов Юникода или байтов UTF-8?

    В любом случае байты UTF-8 делятся на три категории…
    • Начальные: 0x00…0x79, и 0xC0…0xF4
    • Дополнительные (не бывают в начале): 0x80…0xBF
    • Запрещённые: 0xF5…0xFF. В наших целях тоже можно отнести к начальным.

    Если задача — получить 10 символов, то находим 11-й начальный символ и обрезаем перед ним.

    Если задача — получить 10 байт и 11-й (s[10], если таковой есть, разумеется) — не начальный, начинаем урезать строку, пока не отрежем начальный символ.
    Ответ написан
  • Qt. Как передавать данные между формами?

    @Mercury13
    Программист на «си с крестами» и не только
    Вариантов много, в зависимости от того, форма модальная или нет, и нужно ли каким-то моделям передавать сообщение «обновись».

    Вариант 1.
    class AddItem : public QWidget
    {
    public:
      AddItem(QWidget* aParent, QList<Institution>& aInstitutions)
          : QWidget(aParent), institutions(aInstitutions) {}
    private:
      QList<Institution>& institutions;
    };


    Во втором варианте у нас модальная форма, но редактировать можно только копию (например, институции первой формы задействованы в какой-то модели, или нужны каскадные удаления, или что-то ещё).
    class AddItem : public QDialog
    {
    public:
      int exec(QList<Institution>& aInstitutions);
    private:
      QList<Institution> institutions;
    };
    
    int AddItem::exec(QList<Institution>& aInstitutions)
    {
      institutions = aInstitutions;
      int r = QDialog::exec();
      if (r) {
         aInstitutions = std::move(institutions);
      }
      return r;
    }


    И много-много других вариантов.
    Ответ написан
    Комментировать
  • Qt как создать много окон внутри одного?

    @Mercury13
    Программист на «си с крестами» и не только
    Сам не работал с этим под Qt, но называется это «многодокументный интерфейс» и простейший пример тут.
    doc.qt.io/qt-5/qtwidgets-mainwindows-mdi-example.html

    Также можно погуглить «Qt MDI».
    Ответ написан
    1 комментарий
  • Почему возникает ошибка "Нет такого слота"?

    @Mercury13
    Программист на «си с крестами» и не только
    Иногда Qt Creator проглючивает и требуется полная перекомпиляция проекта.
    Почему-то не запустился MOC.
    Ответ написан
  • Зачем нужны матрицы в Direct3D?

    @Mercury13
    Программист на «си с крестами» и не только
    Начнём с того, что вектор имеет три координаты: x, y и z. Все повороты системы координат (x, y, z) → (x', y', z'), масштабирования и их комбинации можно записать в виде

    x' = a11·x + a12·y + a13·z
    y' = a21·x + a22·y + a23·z
    z' = a31·x + a32·y + a33·z

    А теперь прочитайте, что такое «умножить матрицу на вектор». Узнаёте? — матрицу {aij} размером 3×3 умножаем на вектор-столбец (x, y, z)T и получаем вектор-столбец (x', y', z')T. Здесь буква T — это операция «транспонировать матрицу», заменить строки столбцами, а столбцы — строками.

    А теперь сделаем финт ушами. Возьмём 4-векторы (r, s, t, d) и обозначим x=r/d, y = s/d, z = t/d (так называемые однородные координаты). Преобразование в однородные координаты неоднозначно: декартовы координаты (1, 2, 3) можно обозначить как четвёркой (1, 2, 3, 1), так и четвёркой (10, 20, 30, 10). Для чего нам однородные? Матрицы 4×4, работающие над однородными координатами, позволяют записать и такие преобразования, как «сдвинуть» или «центральная проекция». Например, «сдвинуть» записывается как

    r' = 1·r + shiftx·d
    s' = 1·s + shifty·d
    t' = 1·t + shiftz·d
    d' = d

    или, в терминах матриц
    [r']   ( 1 0 0 shiftx )   [r]
    [s'] = ( 0 1 0 shifty ) · [s]
    [t']   ( 0 0 1 shiftz )   [t]
    [d']   ( 0 0 0 1      )   [d]

    (тут я не поленился собрать тэгом code нечто похожее на вектор-столбец, так что знак транспонирования не нужен :)
    Ответ написан
    Комментировать
  • Для чего нужна рантайм-библиотека?

    @Mercury13
    Программист на «си с крестами» и не только
    В рантайм-библиотеке хранятся стандартные функции языка программирования. И malloc/new тоже.

    P.S. Также хранятся внутренние функции для обслуживания точки входа (int main).
    Ответ написан
  • Qt: как сделать, чтобы редактор в таблице вёл себя по-другому при нажатии Tab?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    QModelIndex EditableTable::moveCursor(
            CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
    {
        switch (cursorAction) {
        case QAbstractItemView::MoveNext:
        case QAbstractItemView::MovePrevious:
            return QModelIndex();
            break;
        default:
            return Super::moveCursor(cursorAction, modifiers);
        }
    }


    Чтобы протолкнуть такую функциональность в Qt Creator, использовать функцию «Promote To…»
    Ответ написан
    Комментировать
  • Почему не советуют использовать технологии Borland?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Очень долгое время был устаревший компилятор C++. Насколько мне известно, в XE10, совсем недавно, решилось (не проверял). Слоупоки!
    2. А почему? Где-то с 2001 года Borland натурально начхал на Delphi. Delphi 7 долгое время был самым лучшим — пока не появился 2010. Новые владельцы восстанавливают упущенное, но не всё идёт как надо (XE4 реально глючнее XE2, буду ещё думать, что лучше держать под Win10, чтобы работало с тамошней средой .NET).
    3. Кажется, они поставили не на тот кроссплатформенный фреймворк. FireMonkey — имитация стандартных элементов управления. Медленно и глючно. А если ради чего-то решил выключить ClearType — вообще атас!
    4. Бесплатный WinForms отобрал немалую аудиторию у тех, кому надо быстренько написать программу только под Windows. Но это скорее аналог VCL, чем FireMonkey.
    5. Embarcadero — фактически единственные, кто поддерживает хороший Паскаль.
    Ответ написан
    Комментировать
  • Есть бинарные дерево поиска в С++?

    @Mercury13
    Программист на «си с крестами» и не только
    Именно так работает стандартный контейнер std::map.
    Ответ написан
    Комментировать
  • Как в с++ добавить переменную в обьект уже определённого класса?

    @Mercury13
    Программист на «си с крестами» и не только
    Способ 1. Наследование.
    class Entity
    {
    public:
      virtual ~Entity() {}    // для корректной работы динамических структур данных 
                              // наподобие менеджеров уровней; в нашем примере не нужно;
                              // в реальной игре потребуется
    }
    
    class Player : public Entity
    {
    public:
       bool isDead;
    }
    
    int main()
    {
       Player player;
       player.isDead = true;
       return 0;
    }


    Если кто-то отдаёт Entity, окторый гарантированно Player — то
    Player& somePlayer = dynamic_cast<Player&>(someEntity);


    Способ 2. Композиция
    class Player
    {
    public:
       Entity entity;
       bool isDead;
    }


    Способ 3. Словарь. Это уже на случай, когда чужой код настолько монолитный, что ничем его не прошибёшь.
    struct PlayerInfo
    {
        bool isDead;
    }
    
    std::map<const Entity*, PlayerInfo> playerInfo;


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

    typedef std::string PlayerId;
    std::map<PlayerId, PlayerInfo> playerInfo;


    Если и код монолитный, и «имя» или «значение» объекту никак не придумаешь — тогда никак.
    Ответ написан
    9 комментариев
  • Как присвоить переменную?

    @Mercury13
    Программист на «си с крестами» и не только
    Так, я был явно неправ, что язык — C++ Builder. Если это C++/CLI, то…
    String^ theLine = this->serialPort1->ReadLine();
    Вроде так.
    Ответ написан
  • Должно ли так быть?

    @Mercury13
    Программист на «си с крестами» и не только
    Ошибка у тебя в scanf.

    scanf("%lf",&x);

    %lg тоже работает — по крайней мере, на MinGW.

    Мало известно, но у scanf немного не такой формат подстановок, чем у printf.
    Ответ написан
    2 комментария
  • Почему не возможен синхронизованный вывод двух потоков при использовании двух мутексов?

    @Mercury13
    Программист на «си с крестами» и не только
    А точно нельзя?
    Я бы сделал так…

    Поток 1.
    мютекс2.войди
    поток2.старт
    повторяй 10 раз
      мютекс1.войди
      writeln
      мютекс2.выйди
    поток2.дождись
    мютекс2.выйди

    Поток 2.
    повторяй 10 раз
      мютекс2.войди
      writeln
      мютекс1.выйди

    Обратите внимание, мы входим в один мютекс и выходим из другого. Проверю, отпишусь.

    Единственное спорное «средство синхронизации» — в потоке 1 дождаться завершения потока 2.
    Ответ написан
    3 комментария