Задать вопрос
Ответы пользователя по тегу C++
  • Нормально ли создавать пустую вирутуальную функцию?

    @Mercury13
    Программист на «си с крестами» и не только
    ВСЕ ПРИМЕРЫ ЖИВЫЕ, у меня перед глазами.
    • stream.flush — физический сброс данных на устройство (при выводе) или сброс буферов (при вводе) имеет место только с реальным устройством, а написанные прикладным прогером потоки редко с ними работают, потому пустая.
    • Импортёр из системы управления предприятием, который для каждого, например, рецепта производства вызывает callback. Если рецепты не поддерживаются — ничего не делать.
    • В каком-то виджете Qt напрочь убираем реакцию на колесо мыши.
    • Программа использует для ускорения кучу кэшей, которые перед импортом данных выкидываются. Если выкидывать нечего — функция compactMemory пуста.
    • Или твоя фигура-квадрат, для которой поворот ничего не делает.
    Ответ написан
    Комментировать
  • Лучше ли использовать enum для цвета нежели struct?

    @Mercury13
    Программист на «си с крестами» и не только
    Разрешите вмешаться. Штатное процессорное выравнивание — 8 на x64.

    Выравнивания ниже штатного иногда увеличивают, чтобы одним махом обрабатывать несколько полей. Например, для умножения цветов в формате RGB на x86 кто-то одним махом множил R и B, а вторым — G. Но при этом для производительности цвет должен иметь выравнивание 4.

    Выравнивания выше используют только для хитрых системных целей: выйти за строку кэша, работать с железом, которому такое выравнивание нужно, иногда для оптимизации: улучшенный Doom грузил таблицы преобразования цветов по адресам, кратным 64K, и собственно в EDX грузил адрес, в DH цвет фона, в DL цвет изображения — и получал в EDX указатель на новый цвет.

    Касательно SDL — они в этой функции хотели абстрагироваться от формата цвета (RGB или BGR).

    С enum — всё больше зависит от архитектуры проги: например, вы можете абстрагироваться от реальных значений цветов и иметь цвета «фон», «передний план», «рамка таблицы», «хороший», «плохой». Разница в производительности невелика.
    Ответ написан
    Комментировать
  • Зачем использовать кучу если есть стек? а так же где применяются указатели и ссылки?

    @Mercury13
    Программист на «си с крестами» и не только
    Да, работа с сегментом данных и стеком значительно легче для процессора, и потому их используют везде, где можно. Куча используется в четырёх основных сценариях.
    1. Выделение большого количества данных, которые стек вместить не может. Пример: матрица на много-много мегабайт.
    2. Выделение не известного заранее количества данных. Обычно «не известное заранее» = «возможно, большое», но и тут есть интересный пример: даже если 1000 символов на строки нам хватает за глаза, строки используются настолько часто, что слишком уж расточительно на каждую строку пускать по килобайту.
    3. Сложное время жизни объекта, не ограниченное рамками функции. Примеры могут быть в играх: функцией Spawn объект появляется, функцией Kill уничтожаем.
    4. Использование виртуального полиморфизма (см. ООП). Игровые позиции, как ни странно, плохо совместимы с ООП, так что давайте будет так: функция CreateStream может создать поток, работающий с отрезком памяти или файлом.

    Указатель может иметь такие значения.
    • Единственная «ниточка», ведущая к объекту в куче. Все указатели перенаправили, а объект не уничтожили — всё, кусочек памяти «висит».
    • Некие «стрелочки», налаженные между объектами, чтобы устроить связный список, дерево или что-то ещё. Независимо от того, какие объекты: мы вполне можем в небольшой игре создать кучу объектов с запасом, скажем, в сегменте данных или даже стеке, и налаживать между ними такое же взаимодействие, как будто они в куче.
    • Просто «стрелочка» на кусок памяти, которую принимает или отдаёт функция. Причём эта стрелочка может указывать и в пустоту (nullptr). Всё, что функция изменяет по этой стрелочке, будет видеть и вызывающая программа — в отличие от передачи по значению. Или просто объект такой большой, что его копировать бессмысленно. Или архитектурно некопируемый (системный ресурс вроде открытого файла).
    • Арифметика указателей позволяет работать с кучей объектов, которые сидят рядом в памяти. В Си++ есть понятие «итератор» — объект, похожий на указатель и обладающий арифметикой указателей, но позволяющий пробежаться, например, по дереву поиска.
    • Опять-таки, виртуальный полиморфизм приводит к тому, что к нам приходит не-знамо-что не-знамо-какого устройства (мы лишь знаем, что этот объект, скажем, умеет читать данные как из файла) — обращаться к таким объектам можно только через указатели. В отличие от предыдущего примера с итераторами, мы не понимаем даже самых основ объекта — сколько он байтов занимает, как его создавать, копировать и уничтожать (часто в интерфейс объекта входят столь же виртуальные команды «сделать копию» и «уничтожить», но это отдельный вопрос).

    На ссылку смотрите как на указатель, который 1) не может указывать в пустоту; 2) надо обязательно сразу же присвоить и дальше не перенаправлять. Самое то, когда функция принимает или отдаёт «стрелочку» на кусок памяти, и стрелочка никогда не может указывать в пустоту.
    Ответ написан
    Комментировать
  • Как подключить файл во время работы программы?

    @Mercury13
    Программист на «си с крестами» и не только
    Что значит «подключить файл»?
    Загрузить файл данных? std::ifstream.
    Загрузить динамическую библиотеку? Ищи системные функции вроде LoadLibrary в Windows и dlopen в Linux. (Кроссплатформенных библиотек загрузки DLL/SO мало, а стандарт за этим даже не гонялся.) Но в любом случае потребуются заголовки функций в препроцессорном файле.

    Си++ компилирует в машинный код и не таскает за собой компилятор, и потому динамически подключить файл исходника, как это делает какой-нибудь PHP, не может. Только динамическую библиотеку — какие-то исходники, скомпилированные в машинный код.

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

    Если нужна поддержка скриптов в проге — смотри на Lua. В последнее время очень любят Python. но я не в курсе и один хрен пригодно только для больших прог, готовых таскать с собой дистрибутив Питона.
    Ответ написан
  • Какая сложность у std::sort?

    @Mercury13
    Программист на «си с крестами» и не только
    Символ Ландау g(x) = O(f(x)) при x→y означает: g(x)⩽kf(x) при x, достаточно близких к y. В данном случае y=∞.
    То есть с точностью до константы. А как Wataru сказал, основание логарифма — это умножить на константу.

    А я попытаюсь сказать, почему удобны символы Ландау.
    1. Математическим исследованием сложно выяснить, сколько операций займёт одна итерация цикла — две или тысячу. Этим пусть занимаются микрооптимизации.
    2. Зато в любую программу загонят столько данных, что она сломается. И как правило — если не доказано обратное — асимптотическая сложность быстро пересилит эту самую константу, и программа большей сложности сломается первой.
    3. Ну вот прога сломалась, и мы занялись микрооптимизациями и заменили процессор, и получилось, скажем, вдвое — насколько более крупные задачи мы теперь можем решать? Если O(N), то вдвое более крупные. Если O(N log N), то несколько меньшие, чем вдвое. А если O(N²), то в 1,4 раза.
    Ответ написан
  • Как написать что-то в консоли?

    @Mercury13
    Программист на «си с крестами» и не только
    Итак, надо вызвать прогу и повзаимодействовать с ней. Кроссплатформенность нужна?

    Если работаем на чистом WinAPI, надо пробовать перенаправление потоков ввода-вывода.

    С повышением прав не работал: если повышение не нужно, или прога изначально работает под одминком, никаких вопросов, но ведь всякое бывает…
    Ответ написан
    Комментировать
  • Отличие int32_t от std::int32_t?

    @Mercury13
    Программист на «си с крестами» и не только
    Никакого. int32_t — это из Си, std::int32_t — стандартное пространство имён Си++.
    Ответ написан
    Комментировать
  • Почему появляется лишний символ при открытии файла qt C++?

    @Mercury13
    Программист на «си с крестами» и не только
    А какой код генерации этих данных? Я пока вижу, что 8 байтов длины в формате Motorola (58), после этого 58 байтов данных. Мне интересно узнать, откуда взялся qword длины, и откуда — dword 6 в начале, тоже в формате Motorola?

    UPD. Пока подозреваю, что дело в SendBufferSizeSocketOption.
    Ответ написан
  • Почему имя параметра может совпадать с именем члена класса?

    @Mercury13
    Программист на «си с крестами» и не только
    Это НЕ синтаксический разбор, а семантика языка.
    В большинстве случаев играет роль ближайший идентификатор. Но в инициализаторах снаружи скобок поле объекта (больше ничего там быть не может, только поле)

    #include <iostream>
    
    class Obj {
    public:
        Obj(std::string x)
            : x(std::move(x))
            // 1. Поле, больше ничего быть не может
            // 2. Параметр, как ближайший идентификатор
        {
            std::cout << '[' << x << ']' << '\n';  // Параметр, как ближайший идентификатор
        }
    private:
        std::string x;
    };
    
    int main()
    {
        Obj obj("Habr");
        return 0;
    }
    Ответ написан
  • Как генерировать случайные числа в вычислительной системе?

    @Mercury13
    Программист на «си с крестами» и не только
    Итак, задача — сгенерировать случайные числа по закону Гаусса.
    Если есть Си++11, копай модуль <random>.
    Если нет, простейший способ сгенерировать — F⁻¹(ξ), для этого есть бэ-мэ качественные приближения обратного распределения Гаусса.
    Есть также более «лёгкий» способ генерировать такие числа по два сразу, гугли.
    Ответ написан
    Комментировать
  • Как вшить .mp4 файл в .exe С++?

    @Mercury13
    Программист на «си с крестами» и не только
    На данный момент вы вызываете внешний плеер, причём не важно, какой — системный ассоциированный с расширением MP4. Не забудьте, что в такой ситуации внешнего плеера уже не будет, придётся налаживать свой.

    1. ОС-зависимые ресурсы (например, ресурсы Windows). Поскольку тут у вас и так WinAPI, этот способ вполне катит.
    2. Обёртка Qt или чего-то другого над этими ресурсами: где они есть, ресурсы, а где нет, простые массивы. Использовать, если и без того используете Qt.
    3. Склеить EXE-файл с MP4. Главное — придумать способ разделить их обратно (например, добавить четыре байта длины в конце).
    4. Преобразовать MP4 в байтовый массив, вставить в какой-то CPP.
    5. Вариант 4, но более быстрый в компиляции: Использовать ожидаемую функцию Си #embed.
    Ответ написан
    Комментировать
  • Как переделать код согласно современным стандартам?

    @Mercury13
    Программист на «си с крестами» и не только
    Вы с неинициализированной памятью работаете почти правильно, но…

    Первое. Надо указать выравнивание класса.
    MyClass* arr = (MyClass*) operator new (sizeof(MyClass) * 10,
                                            std::align_val_t(alignof(MyClass)));
    . . .
    operator delete(arr, std::align_val_t(alignof(MyClass)));


    Второе. А нельзя инкапсулировать подобную работу с памятью в класс, чтобы конструктор выделял память и деструктор отдавал? Я сделал вот такое.
    #include <iostream>
    
    class MyClass {
    private:
        const int a;
    public:
        MyClass() = delete;
        MyClass(MyClass&&) = delete;
        MyClass(const MyClass&) = delete;
        MyClass(int a) : a(a) {};
        ~MyClass() = default;
        MyClass& operator=(MyClass&&) = delete;
        MyClass& operator=(const MyClass&) = delete;
        operator int() const { return a; }
    };
    
    template <class T>
    class DynArray {
    public:
        static constexpr std::align_val_t ALIGNMENT { alignof(T) };
        DynArray (size_t n)
            : maxSize(n),
              d((T*) operator new (sizeof(T) * n, ALIGNMENT )) {}
        ~DynArray() {
            std::destroy_n(d, currSize);
            operator delete(d, ALIGNMENT);
        }
        template <class... Args>
            T& add(Args&&... args);
        T& operator [] (size_t i) { return d[i]; }
        const T& operator [] (size_t i) const { return d[i]; }
    private:
        size_t currSize = 0, maxSize = 0;
        T* d;
    };
    
    template <class T> template <class... Args>
    T& DynArray<T>::add(Args&&... args)
    {
        if (currSize >= maxSize)
            throw std::logic_error("[DynArray::add] Overflow");
        auto& where = d[currSize++];
        new (&where) MyClass(std::forward<Args>(args)...);
        return where;
    }
    
    int main() {
        DynArray<MyClass> da(10);
    
        for (int i = 0; i < 10; ++i) {
            da.add(i);
        }
    
        // Что-то сделали с массивом
        for (int i = 0; i < 10; ++i) {
            std::cout << da[i] << '\n';
        }
    
        return 0;
    }


    Ну и третье — в коде есть, но забыл указать — разбирайте API неинициализированной памяти Си++17.

    ЧЕТВЁРТОЕ. Есть один класс, который не перемещает. Поскольку начиная с Си++17 весь STL внутри работает с неинициализированной памятью, получается довольно мило — но хранится ли оно непрерывно, мы никак не узнаем.
    std::deque<MyClass> dq;
        for (int i = 0; i < 10; ++i) {
            dq.emplace_back(i + 42);
        }
        // Что-то сделали с массивом
        for (int i = 0; i < 10; ++i) {
            std::cout << dq[i] << '\n';
        }
    Ответ написан
    2 комментария
  • Почему переменная становится равна 1, если после инициализации класса она была равна 3?

    @Mercury13
    Программист на «си с крестами» и не только
    Вы используете одноимённую локальную переменную в MainProgram().
    MainProgram::m_command_file_len должно равняться нулю ЧЕМУ ПОПАЛО, потому что его никто не трогал.

    UPD. Вы ещё и неправильно используете венгерскую нотацию: префикс f, m или _, оставленный для внутренних переменных, почему-то забрался в локальную.
    Ответ написан
    1 комментарий
  • Как сделать чтоб перснаж не мог проходить через стены?

    @Mercury13
    Программист на «си с крестами» и не только
    Если отбросить в сторону общую упоротость кода (облом делать ревизию), проблема вот в чём.
    Работая с коллайдером (фигурой, которая участвует в проверке столкновений, она же хитбокс), вы выбрали диапазон клеток
    [y / 16; (y + h) / 16).

    Например, y = 1, h = 16, (y+h)/16 = 1, диапозон [0,1), и проверяется только клетка 0. А надо 0 и 1.

    Так что надо [y / 16; (y + h - 1) / 16]. ВКЛЮЧАЯ СПРАВА.
    Ответ написан
    Комментировать
  • Как сделать чтоб одна вункция роботала всегда а другая паралельно каждую секунду?

    @Mercury13
    Программист на «си с крестами» и не только
    Итак.
    1. В играх очень хорош объектный подход — что-то вроде
    class World {
    public:
      bool wasStarPlaced = false;
      bool wasStaircasePlaced = false;
      std::vector<std::vector<char>> map;
      unsigned long long tick = 0;  // пригодится ещё
      void update();  // стандартное название для функции «исполнить игровой такт»
    };

    Тут даже не нужны все каноны ООП, достаточно что-то вроде «поле X принадлежит объекту Y».

    2. Многопоточка чревата гонками по данным, и если в многопоточку не стоит — пожалуйста, не делайте! В данном случае вы хотите что-то вроде: каждую секунду выполнять дополнительную ветку.

    Как я понял, игра динамичная, и расчёты в ней достаточно простые, чтобы вести их с постоянной частотой (например, 60 тактов/с).
    constexpr int TICKS_PER_SEC = 60;
    
    void World::update()
    {
      ++tick;
      if (tick % TICKS_PER_SEC == 0) {
         // происходит каждые 60 тактов
      }
    }
    Ответ написан
    Комментировать
  • Как обеспечивается совместимость динамических библиотек при ликовке в рантайме?

    @Mercury13
    Программист на «си с крестами» и не только
    Устроено просто.
    1. Имена всё равно стараются не козявить. Те библиотеки, где объектный интерфейс выставлен наружу, менее любимы. А уж шаблонных специализаций вообще сторонятся.
    2. А если козявить — есть модель Windows (MSVC, Borland) и модель Linux (MinGW, CLang).
    3. В модели Linux подключение DLL устроено через файл *.a, смысл которого для DLL’ки — сопоставить покозявленное компилятором имя функции и таковое же, выставленное DLL’кой наружу. И для любой DLL’ки можно создать этот *.a с любыми именами. MSVC CL и MinGW LD могут подключать DLL и напрямую, CLang LLD — когда проверял, ещё нет.
    4. Да, а как заменить в скомпилированной программе. Обычно новую версию DLL’ки компилируют тем же компилятором — вот и вся совместимость. В x64 с этим делом проще, чем в x86 — меньше соглашений вызова.
    5. Часто в ABI приходится переименовывать функции из-за того, что сменилась сигнатура. То есть тащат и старую, и новую с разными именами.

    Когда программа собирается одним компилятором, а DLL’ка другим — это всегда большая сложность, и вопроса два: соглашения вызова и правило, по которым козявятся имена.
    Ответ написан
    Комментировать
  • В какого типа переменных хранить адреса?

    @Mercury13
    Программист на «си с крестами» и не только
    Вариантов много.
    1. Непрозрачные указатели, которые нельзя разыменовывать.
    struct OpaqueAddress;
    using Address = OpaqueAddress*;

    2. void*, const void*.
    3. uintptr_t.
    4. Жёсткие int’ы, если работаем с конкретной посторонней прогой под конкретную архитектуру (например, пишем чит к игре).
    using Address = uint32_t;
    5. Enum class, основанный на соответствующем int’е.
    enum class Address : uintptr_t { NUL = 0 };
    Ответ написан
    Комментировать
  • Что стоит учить с или c++ или c#?

    @Mercury13
    Программист на «си с крестами» и не только
    Если ты умеешь программировать на чём угодно, хоть на Скрэтче — можно любой по желанию. Разберёшься.
    Если с нуля — только C#, на нём меньше шансов напортачить. Главная проблема Си, плохо решённая в Си++,— для простых вещей приходится работать со сложными концепциями вроде указателей для scanf.
    Ответ написан
    Комментировать
  • Почему '\xDA', '\xc4', и другие управляющие последовательности не работают?

    @Mercury13
    Программист на «си с крестами» и не только
    Потому что вы перевели консоль в кодировку Win-1251 и и ждёте в ней псевдографику.
    Лучше всего работать в каком-то из вариантов Юникода.
    Ответ написан
    Комментировать
  • Как нормализовать массив значений в цветовое представление?

    @Mercury13
    Программист на «си с крестами» и не только
    Уровень 1. Чёрно-белое.
    R, G, B = round((x−min)·255 / (max − min))

    Уровень 2. Градиент между цветом X и цветом Y.
    t = (x−min) / (max − min)
    R = round(R1·t + R2·(1−t))
    G и B аналогично.

    Уровень 3. Учёт гамма-кривой монитора. Тут работаем сразу в двух цветовых пространствах: линейном от 0 до 1, и sRGB от 0 до 255.
    gamma = 2,2 — sRGB состоит из линейного и степенного участка, но неплохо приближается real_brightness = channel_%^gamma
    invGamma = 1/gamma
    функция toLinear(v) := (v/255)^gamma
    функция toSrgb(q) := (q^invGamma)·255 — в общем, обратная
    linR1 = toLinear(R1)
    linR2 = toLinear(R2)
    t = (x−min) / (max − min)
    R = round(toSrgb(linR1·t + linR2·(1−t)))

    Уровень 3.1. 16-битная аппроксимация (если важна скорость). В общем, линейное цветовое пространство — не дробные от 0 до 1, а целые от 0 до 65535.
    функция toLinear(i) := round(((i/255)^gamma)·65535)
    массив toSrgb(q) := ((q/65535)^invGamma)·255 — записывается в виде массива на 65536 величин
    linR1 = toLinear(R1)
    linR2 = toLinear(R2)
    K = (65535 / (max − min)
    t = round((x−min) * K)
    R = toSrgb(((linR1·t) + linR2·(65535−t) + 127) >> 16)

    Уровень 4. Сложный градиент из нескольких цветов.
    Для этого например, t=0 — синий, t=⅓ — зелёный, t=⅔ — жёлтый, t=1 — красный.
    Тогда прикидываем, в какой промежуток попадает t, получаем, например, t1=(t−⅓)·3, и дальше по уровню 3.
    Ответ написан
    Комментировать