Задать вопрос
  • Расскажите в каких ситуациях используют класс quiloader в QT?

    @Mercury13
    Программист на «си с крестами» и не только
    В крайне-крайне редких случаях, когда вы пишете редактируемый пользовательский интерфейс. Например, в ERP.
    Разумеется, QUiLiader под капотом есть всегда, но среднему программисту он ни к чему.
    Ответ написан
    Комментировать
  • Как программировать игру в игре?

    @Mercury13
    Программист на «си с крестами» и не только
    Обычно для этого используют скриптовый движок. Наиболее известный — Lua.

    Также используют триггеры, эталонный пример — StarCraft.
    Но, работая с триггерами, надо разделять «условие» и «повод». Повод — это событие-импульс, которое МОЖЕТ спровоцировать срабатывание триггера. Условия — это уже дополнительные вещи, которые проверяются потом.

    Третья возможность — движок, основанный на данных. Например, задаём тип атаки, количество HP, тип движения и прочее, и вся эта библиотека уже записана в коде. Эталонный пример — Doom (самый ещё первый, 1993 года). Кстати, в его последователе Hexen появились скрипты.
    Ответ написан
  • Где ошибка в самодельном strcmp?

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

    TCP работает сплошным потоком, и если вы послали в сокет нечто ещё, кроме «123123», он считает весь поток до 2048 байт и, разумеется, строки не совпадут: с одной строны «123123», с другой, например, «123123qwe».

    Вам надо своими силами разбивать TCP-поток на сообщения — например, тем самым нулевым символом, CR или двумя/четырьмя байтами длины пакета.
    Ответ написан
  • Какова очередность присваивания нового объекта?

    @Mercury13
    Программист на «си с крестами» и не только
    А это значит, что манипулируя членами b я буду редактировать память a?

    Нет, конечно.

    Type& Type::operator=(const Type& x);
    Задача операции присваивания — сделать, чтобы объект слева (он же *this) стал копией объекта справа (он же x).

    Почему операция присваивания берёт Type& по ссылке? Да потому, что взять по копии — это вызвать конструктор копирования. Вполне можно написать
    BigInt& BigInt::operator = (int x);
    когда слева «большой» тип, а справа — «маленький», и его конструктор копирования незначителен (а то и равняется накладным расходам на ссылку, как у того же int). Но когда с обеих сторон один и тот же тип — это часто бездумный вызов конструктора копирования (собственные конструктор копирования и операцию = обычно пишут, когда нужна хитрая логика, а не тривиальное копирование).

    Почему операция присваивания возвращает Type&? Просто чтобы работали конструкции типа a = b = c. Может и void возвращать, если хочешь.

    class C
    {
    public:
        C() {}
        C (const C&) { std::cout << "Copy ctor" << std::endl; }
        C & operator = (const C&) { std::cout << "Assign" << std::endl; return *this; }
    };
    
    int main()
    {
        C a;
        C b = a;

    Выведет «Copy ctor». То есть ответ на первый вопрос — конструктор копирования.

    В литературе что я читаю это называется присваиванием

    Не присваивание, а инициализация. Присваиванием было бы…
    Type b;
    b = a;


    Type b = Type(a);

    В идеале тут два конструктора копирования и один деструктор, однако компилятору даётся воля уменьшать их количество. Потому только «copy ctor».

    Код
    int a = 10;
    C b = C(a);

    также даёт один вызов C(int). А C(const C&) не вызывается ни разу.

    UPD. Одно из нововведений Си++11 — чтобы даже без этих негарантированных оптимизаций, когда один объект исчезает, а второй появляется, можно было вместо копирования проводить нечто более простое, не требующее отвода-возврата памяти. Я-то экспериментировал в режиме Си++03, но в Си++11 уже были бы конструктор копирования, конструктор перемещения и деструктор.
    Ответ написан
    Комментировать
  • Почему точность вычислений в float нарушается для чисел определённой степени двойки?

    @Mercury13
    Программист на «си с крестами» и не только
    Связано, вероятно, с особенностями перевода дробных чисел в текст. Я попробовал повторить на Си++, ничего интересного не вышло. Зато методы перевода дробного в текст G++ и GRISU дают несколько разные результаты.
    Ответ написан
    Комментировать
  • В чем здесь ошибка (перегрузка операторов)?

    @Mercury13
    Программист на «си с крестами» и не только
    Вы наладили операцию String << ostream → ostream. Надо наоборот, ostream << string → ostream.

    Такую операцию можно наладить только за пределами класса (возможно, как friend).
    Ответ написан
    1 комментарий
  • Почему sizeof показывает фактический размер массива хотя по сути имя массива это указатель на первый элемент?

    @Mercury13
    Программист на «си с крестами» и не только
    Несмотря на то, что массив часто отождествляют с указателем, массив — это НЕ указатель. У него, например, другие операции приводят к неопределённому поведению.

    И самое противное в Си — это то, что в коде
    void sort(int x[5]);
    x — это не массив, а именно что указатель. Чтобы был массив, надо Си++
    void sort(int (&x)[5]);
    И компилятор даже подсвечивает, что параметр функции, отмеченный как массив — всегда указатель.
    typedef int Arr[SIZE];
    int sum(Arr arr, int n)
    // warning: 'sizeof' on array function parameter 'arr' will return size of 'int *' [-Wsizeof-array-argument]|
    Ответ написан
    Комментировать
  • Как написать визуализацию алгоритма сортировки в delphi?

    @Mercury13
    Программист на «си с крестами» и не только
    longclaps написал, как всё это дело должно выглядеть внешне.
    Внутренне это реализуется просто.
    1. В OnPaint какого-нибудь компонента (PaintBox, скажем) при определённых условиях выводим визуализацию.
    2. Блокируем весь пользовательский интерфейс (кроме какой-нибудь кнопки «Стоп»).
    3. Каждый раз, когда надо что-то увидеть, делаем paintBox.Repaint, Application.ProcessMessages и небольшую задержку.
    4. При нажатии кнопки «Стоп» ставим какую-то переменную в true. Сортировка реагирует на эту переменную и останавливает цикл: либо простым Exit, либо аварией Abort и последующей реакцией на EAbort.

    Если есть желание сравнить несколько алгоритмов сортировки, стоит сделать функцию типа DoSwap(i, j : integer), которая меняет местами элементы, устанавливает всё, что надо для визуализации, инициирует перерисовку и делает задержку.
    Ответ написан
    Комментировать
  • Что значит эта строчка?

    @Mercury13
    Программист на «си с крестами» и не только
    If the most significant bit is set, the key is down, and if the least significant bit is set, the key was pressed after the previous call to GetAsyncKeyState.

    Проверка на младший бит (сокращённая запись if ((GetAsyncKeyState(VK_F1) & 1) != 0). Он означает: с предыдущего вызова GetAsyncKeyState клавиша хоть раз нажималась. С одной стороны, этот бит нерекомендуемый (работает, когда нет других программ, вызывающих GetAsyncKeyState). С другой, возможно, младший бит GetAsyncKeyState() в современной реализации Windows работает именно так, как надо (если программа неактивна, GAKS возвращает 0).

    GAKS обычно используется в играх и прочих динамичных программах; в рабочем ПО используют оконные сообщения.
    Ответ написан
    1 комментарий
  • Как уменьшить связанность классов?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Что такое Container и для чего он нужен? Возможно, от этого дела удастся избавиться или заменить интерфейсом?
    2. Не должен конструктор Graph брать в параметры Parser. Наоборот, Parser функцией parse() должен возвращать Graph.
    3. Config стоит разбить на несколько частей: одна специфична для Graph, вторая для Parser. Как их объединять — зависит от того, кому какие настройки нужны.
    Ответ написан
    2 комментария
  • Есть ли разница между двумя функциями?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Будьте осторожны, перед нами динамический массив, сделанный расширением стека. Не на всех компиляторах есть, плюс стек ограничен парой мегабайт.
    Нормализацию размера лучше сделать shift %= size;
    Подпрограмма работает с локальной переменной и всё, что она делает, идёт на экран и больше никуда.
    (вообще стоит разделять обработку и вывод!)
    Время работы size.

    2. Сдвигаем на один элемент нужное количество раз. Время работы shift·size.

    3. Как за size «на месте»…
    квоПрох := НОД(shift, size)
    для i = [0..квоПрох)
      i1 := i
      tmp := a[i]
      вечный цикл
        i2 := (i1 + shift) % size
        если i2 = i
          прервать вечный цикл
        a[i1] := a[i2]    
        i1 := i2
      a[i1] := tmp
    Ответ написан
    Комментировать
  • Как создать "библиотеку" для NPC на языке C++?

    @Mercury13
    Программист на «си с крестами» и не только
    Из такого вопроса видно, что человек всё ещё нетвёрдо знает программирование, а уже пытается программить игру.
    И тут есть два варианта.

    1. Движок с нуля. Тонкая графическая прослойка наподобие SDL, оставляющая 100%-ю свободу движкописателям — это тоже «с нуля» и всячески рекомендуется даже продвинутым (не нужно писать всякие там Alt-Tab’ы и прочую дрянь, далёкую от игры). Тогда пусть начнёт с «питончика» (https://ru.wikipedia.org/wiki/Snake_(игра) ) или текстового квеста.
    А потом, когда доберётся до платформера или CRPG, он поймёт, каким образом привязать к монстру характеристики.

    Первое приближение (не слишком удобное для скриптования и подходящее для платформеров/action-RPG, но негодное в настоящих RPG) — сделать каждому компьютерному персонажу «класс» (не путать с классом ООП), включающий кол-во HP, внешний вид и прочее. А также «набор скриптов» — для поведения в диалогах, запрограммированного передвижения, превращения из «болванчика» в компьютерного врага, и т.д. Без набора скриптов он просто компьютерный враг, тупо нападающий на ПиСя.

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

    2. На чужом движке. Это уж смотрите, что движок может, какие возможности в нём «в коробке», а какие придётся дописывать своим кодом и чужими модулями. Не знаком ни с одним. Чужой движок — довольно противное дело для начинающего программиста: он автоматически формирует какую-то структуру программы, и программисту приходится не столько творить, сколько разбираться с этой структурой. С одной стороны, можно быстро написать довольно сложную игру, с другой — не так много радости «а у меня заработало».
    Ответ написан
    Комментировать
  • Как реализовать метод буквенного номера по числу?

    @Mercury13
    Программист на «си с крестами» и не только
    Вот мой действующий код на «Си с крестами». Индексы — ВНИМАНИЕ — начинаются с нуля. Сумеете перевести на Яву, и из второго оставить одни буквы? Первое действует для любой длины, второе — для длины до 3 символов, присущей Excel’ю.

    std::wstring xl::colNameW(
            size_t aIndex)
    {
        wchar_t q[21];  // more than enough even for 64-bit system :)
        wchar_t* ptr = q + 20;
        *ptr = 0;   // debug
    
        // Getting size
        size_t n = 1;
        unsigned long long pw = 26;
        while (aIndex>=pw && n<20)
        {
            aIndex -= static_cast<size_t>(pw);
            pw *= 26;
            ++n;
        }
    
        FOR_S(i, 0, n)  // макрос, означающий for (size_t i = 0; i < n; ++i)
        {
            *(--ptr) = static_cast<wchar_t>(L'A' + (aIndex % 26));
            aIndex /= 26;
        }
        return std::wstring(ptr, n);
    }
    
    namespace {
    
        bool isCap(const char* x, const char* aEnd)
        {
            return (x != aEnd && *x >= 'A' && *x <= 'Z');
        }
    
        bool isDig(const char* x, const char* aEnd)
        {
            return (x != aEnd && *x >= '0' && *x <= '9');
        }
    
    }   // anon namespace
    
    
    xl::CellCoords xl::parseCellCoords(
            const char* aStart, const char* aEnd)
    {
        enum {
            AA = 26,
            AAA = 26 + 26 * 26
        };
        xl::CellCoords r;
        // Letter
        r.col = 0;
        if (!isCap(aStart, aEnd))
            return CellCoords::bad();
        r.col = *(aStart++) - 'A';
        if (isCap(aStart, aEnd)) {
            r.col = (r.col * 26) + *(aStart++) - 'A';
            if (isCap(aStart, aEnd)) {
                r.col = AAA + (r.col * 26) + *(aStart++) - 'A';
            } else {
                r.col += AA;
            }
        }
        // Number
        r.row = 0;
        while (isDig(aStart, aEnd)) {
            size_t r0 = r.row;
            r.row = r.row * 10 + *(aStart++) - '0';
            if (r.row < r0)
                return CellCoords::bad();
        }
        if (r.row == 0 || aStart != aEnd)
            return CellCoords::bad();
        --r.row;
        return r;
    }


    Принцип действия перевода цифра → буква.
    Находим, сколько букв в нашей колонке. Допустим, три. Вычитаем из цифры номер колонки AAA (т.е. 26 + 26·26, если нумеровать с нуля, и 1 + 26 + 26·26 — если с единицы), и оставшееся переводим в 26-ичную систему счисления (A=0, B=1, C=2…).

    Принцип действия перевода буква → цифра аналогичен. Находим количество букв. Если их три, то переводим из 26-ичной системы счисления в цифру и прибавляем номер колонки AAA.
    Ответ написан
    Комментировать
  • Возникает ошибка Ошибка C4700 использована неинициализированная локальная переменная как исправить?

    @Mercury13
    Программист на «си с крестами» и не только
    #include "stdio.h"
    #include "stdlib.h"

    Лучше это писать в угловых скобках.

    double dr(double zb)
    {
        return zb = (rand() % 100) / (100 * 1.0);
    }

    Присваиваем параметру zb что-то, чтобы его похѣрить? Параметр будет жить до конца функции и НЕ ВЕРНЁТСЯ в вызвавшую программу (параметры передаются по копии).
    Кстати, параметр zb ни на что не используется: что бы вы ни передали туда снаружи, подпрограмма в этот параметр не посмотрит.

    printf("%lf", dr(double zb));
    На G++ не скомпилировалось. Ну, написал dr(0), благо параметр не используется ни на что.

    if (d == e)
    {
        printf("Win! your cash:%lf", winWay(a, b));

    C:\TestApps\ErrUninitLocal\main.cpp|30|warning: 'd' may be used uninitialized in this function [-Wmaybe-uninitialized]|
    А действительно, переменная d нигде никому не присваивается.

    И ещё одно. Я не знаю, как действует Visual C, но модификатор l для формата %f нужен для scanf, но не нужен для printf. Вот так-то всё это дело устроено.

    double a = winWay(a, b);
    }

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

    C:\TestApps\ErrUninitLocal\main.cpp|33|warning: 'a' may be used uninitialized in this function [-Wmaybe-uninitialized]|
    Тут вообще забавно работает. double a — считаем, что a определилась. = winWay(a, b) — считаем, что используется ОНА ЖЕ, а не более ранняя a. Естественно, неинициализированная.

    (аналогично с loseway)
    C:\TestApps\ErrUninitLocal\main.cpp|38|warning: 'a' may be used uninitialized in this function [-Wmaybe-uninitialized]|
    Ответ написан
    Комментировать
  • Как вернуть ссылку/указатель на элемент вектора?

    @Mercury13
    Программист на «си с крестами» и не только
    1.
    bool FindElement(const string& search, CElement*& result);

    2. CElement* FindElement(const string& search);
    Ответ написан
    1 комментарий
  • Что такое безсерверная обработка данных?

    @Mercury13
    Программист на «си с крестами» и не только
    Название странное. Это когда облачная платформа выделяет не сервер (виртуальную машину, контейнер), а какую-то функцию, и на каких серверах она работает — не важно.
    https://en.wikipedia.org/wiki/Serverless_computing
    Ответ написан
    Комментировать
  • Что такое 3D математика?

    @Mercury13
    Программист на «си с крестами» и не только
    Векторная алгебра (ну или векторная геометрия) в 3D.
    Векторы, матрицы, базисы, однородные координаты, видовые преобразования, кватернион поворота…
    Первый курс университета, покрывается курсом «Линейная алгебра и аналитическая геометрия».
    Дизайнеру не нужно.
    Ответ написан
    4 комментария
  • Нужно ли создавать интерфейсы для одного класса?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Если из класса можно вытащить какую-то абстракцию. Например, из объекта «файл» можно вытащить абстракцию «поток». Личное — объект Project реализует интерфейс Modifiable с двумя функциями: modify() и isModified().

    2. Для упрощения юнит-тестирования при условии владения.
    Предположим, у нас есть класс «класс» (школьный) и класс «ученик». Ученик знает, в каком он классе.
    В такой ситуации получается «клубок»: если надо делать ученика, то надо делать и класс.
    Этот замкнутый круг можно разорвать, сделав интерфейс ISchoolClass и унаследовав от него класс. При юнит-тестировании заменяем класс на какую-то заглушку.
    Ответ написан
    Комментировать
  • Можно ли организовать связь между QT, mySQL и Excel?

    @Mercury13
    Программист на «си с крестами» и не только
    Qt и MySQL работают отлично и очень быстро. Маленькая подсказка: помимо DLL драйвера, который надо бросить в подкаталог plugins\sqldrivers, надо положить в РАБОЧИЙ каталог программы DLL MySQL или MariaDB (зависит от сборки Qt).

    С Excel’ем сложнее, и приходится искать любую Excel-библиотеку, имеющуюся на Си++.
    Из открытых — XLNT (кроссплатформенный) и QtXlsx (привязан к системе классов Qt).
    Мы используем частично LibXL (платный, тормозной, огромный расход памяти, но QtXlsx ещё хуже, насколько я проверял), частично свой велосипед (минимум функциональности, оптимизирован под огромные XLSX — вплоть до того, что Excel берёт большую таблицу за 10 секунд, а мы за три, а LibreOffice вообще над ней размышляет минутами).

    Но это уже собственно вопрос: а что есть для Excel’я на Си++. Может, сырой XLNT довели до ума. Может, у вас нет огромных таблиц, и того, что есть, вам хватает…

    UPD. Есть разные сборки DLL MySQL, так что придётся экспрериментировать, чтобы работало не только на разработческой машине, но и у потенциального юзверя.
    Ответ написан
    Комментировать
  • Как удалить часть строки в c++?

    @Mercury13
    Программист на «си с крестами» и не только
    Так и надо, но не хватает только одного: каким-то образом обрезать строку после всего этого.
    for (…)
      s1[i] = s1[i + s2len]
    s1.resize(s1.length() - s2len);

    Не забывайте: если длина вашей строки — это strlen, то length() в цикле вызывать запрещено!

    Да, ещё вопрос: убрать ПЕРВОЕ вхождение или ВСЕ вхождения?
    Ответ написан