Ответы пользователя по тегу C++
  • Как получить нужную точность при умножении и делении чисел типа double?

    @Mercury13
    Программист на «си с крестами» и не только
    Дело в том, что 4,2 и 4,3 невозможно представить в виде double. И система, например, сохраняет 4,3 в виде 4,2999999, которое при умножении на 10 станет 42,999999.
    Нужно убедиться, что при преобразовании double → int происходит round, а не усечение.
    Ответ написан
    2 комментария
  • Насколько отвратителен данный код?

    @Mercury13
    Программист на «си с крестами» и не только
    Главная проблема.
    const char *source = fmt::format("/tmp/{}.ini", unid).c_str();

    Объект fmt::format сразу же исчезнет, и source будет «висеть».

    Придётся писать…
    fmt::format fmSource("/tmp/{}.ini", unid);
    const char *source = fmSource.c_str();

    И так далее.
    Ответ написан
    8 комментариев
  • Какой формулой определить где находится персонаж - за другим персонажем, перед, справа-слева и тд.?

    @Mercury13
    Программист на «си с крестами» и не только
    Считаем, что у нас FPS, т.е. есть некая абсолютная «горизонталь». Обычно в таких играх взгляд хранится через углы (yaw, pitch, roll).
    Взад-вперёд: знак скалярного произведения двух векторов: единичного вектора взгляда без учёта тангажа (cos(yaw), sin(yaw)) и вектора на персонажа (x1−x0, y1−y0).
    Влево-вправо: знак косого произведения той же пары векторов.
    Вверх-вниз: и так понятно.
    Все три в одних единицах, и у кого модуль больше, то и пишем: сверху, справа и т.д. Возможно, придётся сделать скидку на рост персонажей и их диаметр.

    В играх типа Descent нет верха и низа, горизонтали и вертикали. Верх и низ есть только относительно кабины нашего аппарата, а взгляд хранится в виде ортонормированной матрицы, куска этой матрицы или кватерниона.
    1) Скалярное произведение единичного 3D-вектора взгляда (обычно в таких играх хранится напрямую или считается через кватернион поворота) и 3D-вектора на персонажа.
    2) Скалярное произведение единичного вектора, который смотрит вбок (если такой есть), и вектора на персонажа. Либо смешанное произведение единичного вектора взгляда, единичного вектора макушки и вектора на персонажа.
    3) Скалярное произведение единичного вектора макушки и вектора на персонажа.

    Смешанный случай — с переменным вектором гравитации (в глобальных авиасимуляторах или играх на астероидах) — распиши сам.
    Ответ написан
    Комментировать
  • Как работает криптор который из exe может сделать doc или подобные форматы?

    @Mercury13
    Программист на «си с крестами» и не только
    Есть две разные задачи.
    1. Просто. Пронести программу через систему безопасности при условии, что твой компьютер — «тихая гавань» и можно спокойно декодировать и запустить. Например, чтобы сбить с толку антивирус Gmail.
    2. Сложно. Твой компьютер — уже не «тихая гавань», на нём действуют системы безопасности, и требуется их как-то обойти. Или, например, мы распространяем вирус, а в роли системы безопасности — бдительный пользователь.

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

    Чтобы решить вторую задачу, надо в программе (Word, WinRAR и т.д.) найти эксплойт типа «исполнение произвольного кода».
    Ответ написан
  • Как организовать структуру классов?

    @Mercury13
    Программист на «си с крестами» и не только
    Strategy[N] хранит не наследников Strategy, а объекты класса Strategy. Поэтому в массиве придётся хранить не тела объектов, а указатели.

    Если у вашего МК есть полноценное управление памятью с операцией new, всё просто: std::unique_ptr<Strategy> strategies[N]. Если нет — приходится как-то извращаться, и об этом не будем.

    Как сериализировать? Добавим классу Strategy небольшую функцию
    unsigned char code() const;
    Пусть первая стратегия возвращает, например, 1=CODE_DUPLICATE, а вторая — 2=CODE_MACRO. Сериализация, в числе прочего, будет записывать в память эти коды.

    Считав код, мы создаём Duplicate или Macro, а затем считываем объект этого класса.
    std::unique_ptr<Strategy>& thatObject = strategies[i];
    switch (getSomeByte) {
    case CODE_DUPLICATE:
      thatObject = new DuplicateStrategy();
      break;
    case CODE_MACRO:
      thatObject = new MacroStrategy();
      break;
    default:
      // можно ничего не делать. Можно сообщить об ошибке.
    }
    thatObject->read();
    Ответ написан
    Комментировать
  • Как сделать разную реализацию одной и той же функции класса в C++?

    @Mercury13
    Программист на «си с крестами» и не только
    Перенести пользовательскую функциональность в другое место — так называемый «слушатель».
    using EvClick = void (*)();
    
    Class Model{
    public:
      void click() { if (fOnClick) fOnClick(); }
      void setOnClick(EvClick x) { fOnClick = x; }
    private:
      EvClick fOnClick = nullptr;
    }

    Подобные слушатели есть в любой визуальной оконной библиотеке: VCL, Qt. В VCL так и есть, за исключением вписанных в синтаксис свойств. В Qt для этого используют сигналы-слоты.

    Наладить передачу любых данных в эту функцию — шаблон «команда».
    class ClickEvent {
    public:
      int x, y;
      virtual ~ClickEvent();
    }
    
    using EvClick = void (*)(ClickEvent&);
    Ответ написан
    7 комментариев
  • Как получить адрес наследника?

    @Mercury13
    Программист на «си с крестами» и не только
    Именно так и делать. Потому что от вас требуется наладить шаблон «Public Морозов» в очень тяжёлых условиях (нет даже заголовка). Так что вам придётся повторять то, что обычно делают компилятор и линкер.

    Может помочь устройство таблиц виртуальных методов и dynamic_cast именно в вашем компиляторе. Например, на MinGW у меня получилось вот такое.
    #include <iostream>
    
    
    class A {
    public:
        int a;
        virtual void doA() {}
        virtual ~A() = default;
    };
    
    // class B, C, D, E  аналогично {
    
    class DE : public D, public E { int de; };
    class AB : public A, public B { int ab; };
    class CDE : public C, public DE { int cde; };
    
    class All : public AB, public CDE {};
    
    
    struct Vtable {
        uint32_t d[1];
    };
    
    union PObject {
        void* asVoid;
        Vtable** asVtable;
        char* asRaw;
    };
    
    int main()
    {
        All all;
        All* pAll = &all;
        E* pThis = &all;
        std::cout << pAll << " " << pThis << std::endl;
    
        PObject pX;
        pX.asVoid = pThis;
        while (true) {
            uint32_t offset = -(*pX.asVtable)->d[-2];
            std::cout << "Got offset " << offset << std::endl;
            if (offset == 0)
                break;
            pX.asRaw -= offset;
            std::cout << "Got pointer " << pX.asVoid << std::endl;
        }
    
        return 0;
    }

    До промежуточного потомка (DE или CDE) достучаться не удалось.
    Ответ написан
    3 комментария
  • Почему результат выводится inf?

    @Mercury13
    Программист на «си с крестами» и не только
    У нас тут переполнение, 36! не влезает в тип int. Сделайте хотя бы double.

    Inf («машинная бесконечность») — обычно результат дробного деления на ноль. Почему? Посчитаем, сколько двоек в 36!
    Делятся на 2 — 18 сомножителей.
    И ещё на 4 — 9 шт.
    И ещё на 8 — 4 шт.
    И ещё на 16 — 2 шт.
    И ещё на 32 — 1 шт.
    Итого в составе 36! будет 34 двойки, то есть 36! делится на 234. Разумеется, целочисленная арифметика, работающая в кольце остатков от деления на 232, даст ноль. При этом, разумеется, 32-битный регистр многократно и безнадёжно переполнится.

    Более сложный способ расчёта ряда — рекуррентное соотношение. Очередное слагаемое поделим на две части: ai = (2i)! / i²! и bi = |x| / i²!. Постройте, как соотносятся ai−1 и ai, аналогично для b.
    Ответ написан
    Комментировать
  • Почему после определения класса ставится точка с запятой, а после определения функции - нет?

    @Mercury13
    Программист на «си с крестами» и не только
    Это пошло ещё с Си. Так что, простите, ничего плюсового не будет, только «няшная сишка».
    // Переменная безымянного типа
    struct {
      int x, y;
    } point;
    
    // Синоним для типа: вместо struct _Point можно писать просто Point.
    typedef struct _Point {
      int x, y;
    } Point;
    Ответ написан
    Комментировать
  • Приведение типов указателей, как перегрузить?

    @Mercury13
    Программист на «си с крестами» и не только
    Для этого есть специальная операция, dynamic_cast.
    myClass = dynamic_cast<MyClass*>(interface);
    Если interface не MyClass, то вернёт NULL.

    В случае, если через dynamic_cast присваиваем не указатели, а ссылки, выбросит аварию std::bad_cast.

    UPD. Такие преобразования «вниз» — это компромисс между ООП и реальностью, которая вынуждает упрощать интерфейсы. Например, в C++ Builder
    int __fastcall SomeObject::ButtonClick(TObject* Sender)
    {
      TButton* button = dynamic_cast<TButton*>(Sender);
      ...
    }
    Ответ написан
    9 комментариев
  • В чем различие между объявлением с new и без него?

    @Mercury13
    Программист на «си с крестами» и не только
    ПОЛОЖЕНИЕ В ПАМЯТИ
    Без new: static/глобальная — в сегменте данных, локальная — на стеке. В сегменте данных память отводится при компиляции линковке, создать стековый фрейм — две команды процессора.
    С new: в куче. Управление кучей — довольно сложная задача, и если этих new много, программа может начать тормозить.

    ВРЕМЯ ЖИЗНИ
    Без new: объект живёт, пока выполнение находится в данном блоке. При выходе из блока автоматически срабатывает деструктор.
    С new: уничтожаем, когда хотим.

    ИМЕНОВАНИЕ
    Без new: объект привязан к своему имени.
    С new: объект безымянный (имя только у указателя). Потому возможны структуры данных переменного размера: динамические массивы, связанные списки, деревья и прочее.

    РАЗМЕР
    Без new: задан при компиляции. То есть массив на 10 позиций, и точка, больше — только перекомпиляцией.
    С new: произвольный.
    Ответ написан
    Комментировать
  • Как в с++ вывести данные в stdout?

    @Mercury13
    Программист на «си с крестами» и не только
    Вам, как я понял, нужен standard I/O redirection — чтобы то, что мы пишем куда-то, служило входом для процесса FFMPEG, заранее запущенного. В Windows делается это через каналы (pipes) и CreateProcess.
    https://msdn.microsoft.com/en-us/library/windows/d...
    Ответ написан
    Комментировать
  • Что произойдёт после move-касте содержимого shared_ptr?

    @Mercury13
    Программист на «си с крестами» и не только
    После move-каста содержимого shared_ptr мы говорим: объект под shared_ptr идёт на заклание, и в операциях перемещения мы можем потрошить его как угодно — лишь бы объект остался корректным. В данном коде (как я уже писал, он не компилируется) у нас одни ссылки, и не будет ровным счётом ничего: не выполнится ни одна операция перемещения.

    Однако возможна такая ситуёвина, никак не связанная с временными ссылками. Во втором есть неуправляемая ссылка на первый — и при определённых условиях она может стать висячей.

    Если в T2 всё-таки не ссылка, а экземпляр (T1 m_var), возможно, операция перемещения оставляет объект под ptr в некорректном состоянии, и деструктор ~T1() не может его уничтожить, или какая-то из операций над ptr не подозревает, что объект под ptr выпотрошен.

    UPD. Что делает updateSearchResults? Только он может продлить время жизни объекта, сохранив где-то shared_ptr.

    UPD2. Константная временная ссылка (const T&&) никакого смысла не несёт (по факту в STL пару раз используется). «Неизменный» и «можно выпотрошить» — это слегка противоречивые вещи.
    И всё-таки я грешу на updateSearchResults, который сохраняет где-то объекты. Проблема может решиться, если в PoiSearchPlacesLocation ссылку заменить на shared_ptr. И — ВНИМАНИЕ — соответствующим образом подкорректировать сигнатуру конструктора. У shared_ptr есть конструктор, который берёт под управление любой объект-указатель, создавая новый счётчик, но хреново будет, если один и тот же объект управлять двумя разными счётчиками.
    Ответ написан
  • Какую библиотеку использовать для построения простых графиков?

    @Mercury13
    Программист на «си с крестами» и не только
    Если Qt — то QWT.
    Ответ написан
    1 комментарий
  • Как исправить данный алгоритм поиска максимального подмассива?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Учите, что означает операция «запятая». Если вы хотите выдать наружу три числа — так и пишите возвращаемым типом не int, а структуру из трёх чисел.
    2. Передавать vector по значению невыгодно, лучше по константной ссылке.

    struct ArrayOut {
      int low, high, sum;
    
      ArrayOut() : low(0), high(0), sum(0) {}
      ArrayOut(int aLow, int aHigh, int aSum) : low(aLow), high(aHigh), sum(aSum) {}
    };
    
    
    ArrayOut Find_Max_Crossing_Subarray(const vector<int>& A, int low, int mid, int high)  { ... }


    UPD. Операция «запятая» — это странный хак Си, позволяющий впихнуть несколько операторов туда, где разрешён только один. В основном в цикл for.
    Ответ написан
  • Java или C++ в качестве первого языка. Что выбрать?

    @Mercury13
    Программист на «си с крестами» и не только
    Только Java. Почему…
    1. Достаточно удачная стандартная библиотека.
    2. Проверка массивов на индекс — для начинающего бесценно.
    3. Не настолько полагается на указатели, когда человек ещё толком не знает, что это такое.
    4. Очень строгая проверка типов.
    5. Статическая типизация, Си-подобный синтаксис (хотя всё это у обоих).
    6. Сообщения об ошибках не настолько страшны (стандартная библиотека Си++ полагается на шаблоны).
    Вторым языком человек уже съест что угодно, но Си без плюсов — один из худших первых. Си++ не так плох, но Java, по-видимому, лучше.
    Ответ написан
    4 комментария
  • Как решить ошибку LNK2019?

    @Mercury13
    Программист на «си с крестами» и не только
    Судя по всему, эти функции в библиотеках OpenGL. Надо включить в проект opengl32.lib (и, возможно, glu32.lib).
    Ответ написан
  • Как передать вывод?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Чтобы получить ссылку на CMD.EXE, прочитайте переменную окружения COMSPEC.
    2. Не используйте ShellExecute для запуска того, что гарантированно программа, для этого есть CreateProcess.
    3. Да, и не забывайте, что ShellExecute заканчивает работу, когда программа пошла на исполнение. Если нужен результат исполнения — всё тот же CreateProcess + ввод-вывод через каналы + WaitForSingleObject.
    Ответ написан
    Комментировать
  • Принцип работы анимации?

    @Mercury13
    Программист на «си с крестами» и не только
    time — это длительность такта (в игре, очевидно, не просто переменный FPS, но и переменная тактовая частота). Писал такое, правда, не в платформере, а в гоночке.

    rect.left — это точное решение дифура left′ = dx (движение с постоянной скоростью). Очевидно, dx где-то устанавливается по управлению.

    rect.top и dy — это приближённое решение дифура top′′ = 0,005, если снят флаг onGround (полёт под действием силы тяжести). Здесь 0,005 — это ускорение силы тяжести, ось Y направлена вниз. Дифур второго порядка, преобразуется в систему top' = dy, dy′ = 0,005, и то, что в первом случае было точным, здесь приближённое, но приемлемое для игр.

    currentFrame просто прокручивается, чтобы 6-кадровая анимация прошла за 1200 тактов, независимо от частоты. Здесь 1200 = 6 / 0,005.

    Заметьте, dx и dy проходят по разным трактам данных: один — состояние управления, второй — состояние физики персонажа. К тому же что делает dx = 0 и где он ставится не в 0, чтобы не зависеть от частоты автоповтора клавы — непонятно. И много магических констант. Говнокод.

    UPD. Прямые ответы на ваши вопросы.
    1) 6-кадровая анимация, но тут одно из двух. Либо из-за огромного цикла (1200 тактов) явно анимируются не ноги. А может, афтар гонит такты с предельной частотой и потому 1200 тактов действительно пройдут за секунду-две, но тогда это слишком уж явная привязка к скорости компьютера;
    2) в зависимости от номера кадра выбираем тот или иной спрайт на атласе;
    3) dy — скорость, и чтобы откорректировать положение (rect.y), надо прибавить к нему скорость·время.

    UPD2. Кроме того, непонятно, почему в коде анимации нет бега в разные стороны. Хотя из-за крайне простой физики есть подозрение, что жанр — бесконечная бегалка.
    Ответ написан
    3 комментария
  • Нету вывода из массива с указателями из структуры с++?

    @Mercury13
    Программист на «си с крестами» и не только
    Первое и главное. Удивительно, что у вас программа заработала, ведь text.sentences не инициализировано. У меня вылет.

    Ну и извечная ошибка начинающего «плюсовика»: память выделяется, непонятно, кто чем владеет, и, разумеется, память «течёт». Для чего, извините, в Си++ сделали инкапсуляцию, конструкторы и деструкторы?
    Ответ написан
    Комментировать