Ответы пользователя по тегу C++
  • Как к четырем элементам массива добавить ещё два?

    @Mercury13
    Программист на «си с крестами» и не только
    Нужен или массив с запасом (например, на 6 мест), или динамический (например, std::vector).
    Си++ — язык достаточно низкого уровня, и сам программист видит, где там память динамически выделяется, а где выделена заранее.
    Ответ написан
    2 комментария
  • Объясните разбор файла mainwindow.h в QT по шагам?

    @Mercury13
    Программист на «си с крестами» и не только
    // Защита от повторного включения
    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    // Пара include’ов
    #include <QWidget>
    #include <QMainWindow>
    
    // Определение UI-класса наперёд, чтобы визуальное редактирование не приводило
    // к крупным перекомпиляциям
    namespace Ui {
    class MainWindow;
    }
    
    // Собственно класс формы
    class MainWindow : public QMainWindow
    {
        // Макрос, который добавляет файл в компиляцию MOC’ом,
        // а также реализует пару функций
        Q_OBJECT
    
    public:
        // Конструктор-деструктор.
        // Форма семантически не эквивалентна parent’у,
        // потому конструктор explicit
        explicit MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    // Слоты — фишка Qt, которая обрабатывается MOC’ом
    private slots:
    
    
    private:
        // Указатель на UI (для него в Qt есть ещё одна программа, UIC)
        Ui::MainWindow *ui;
    };
    
    // Защита от повторного включения
    #endif // MAINWINDOW_H


    Что ещё написать?

    Определение класса наперёд не мешает писать указатели и ссылки.

    Конструктор explicit запрещает неявное преобразование. Explicit имеет смысл, если возможен вызов конструктора с одним параметром. Правило таково: ставь explicit, если твой объект по смыслу не эквивалентен единственному параметру конструктора. Например, Ratio(int x);, но explicit Array(int x);: при построении рационального числа из целого будет полный эквивалент, но при построении массива — нет.
    Ответ написан
    Комментировать
  • Почему пробел заменяется на W?

    @Mercury13
    Программист на «си с крестами» и не только
    Пробел не isupper.

    Расчёт тут идёт в знаковом типе, потому *i + key - 97 < 0

    Существует два метода деления с остатком, в x86 встроен тот, где знак остатка равняется знаку делимого (а неполное частное — результат округления к нулю). Именно его впоследствии кодифицировали в Си99. Значит, (*i + key - 97) % 26 < 0

    Это значит, что результат преобразования будет меньше 97 = 'a'. Например, 'W'.

    Ах да. В Си можно написать (*i + key - 'a') % 26 + 'a'.
    Ответ написан
    Комментировать
  • Как быстро выбирать подстроку у строки?

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

    @Mercury13
    Программист на «си с крестами» и не только
    typedef struct ttime {
    Си, на ++ не нужен typedef.
    И используй наконец тэги для кода.

    int hours; int minute; int sec;
    Странное именование, обычно пишут hour.

    struct date {
    ttime time;

    которая в свою очередь входит в состав структуры Date.
    Странно, ведь это не дата, а скорее Clock — часы.

    Методы позволяют изменять текущее время

    Ну и где тут измерение?

    Моя архитектура.
    1. Время. Получение времени из системы, сравнение, вывод в консоль.
    2. Часы. Хранят последнее время. Команда «tick» — получить время, сравнить с последним, при неравенстве — сохранить новое и вывести его в консоль.
    3. Внешний цикл (может быть как в часах, так и снаружи). Часы тикают, делают небольшую задержку и проверяют на нажатие клавиши.
    Пять объектов никак не будет, да и Date нужно только в том случае, если вы реально работаете с датой. Я бы, чтобы докинуть количество объектов до нужного, сделал бы дату (date), время (time) и дату-время (stamp).

    Для турнира единоборств (для простоты — без весовых категорий).
    • Fighter: имя, всякая информация про него вроде города, клуба и титулов.
    • Stub: указатель на Fight + enum (Winner/Loser) — откуда берётся участник. Указателя на Fight нет — тогда TBD (то есть не определился).
    • Participant: указатель на Fighter + структура stub.
    • Fight: номер, два Participant, дата/время, кто победил (0/1), причина победы (ещё не проводился, KO, TKO, по очкам, неявка…)
    • Tournament: содержит список боёв и таблицу результатов.

    Можно также разорвать кольцо зависимостей, наладив интерфейс FightInfo, сделав в Stub FightInfo*, а не Fight*, и чтобы Fight реализовывал FightInfo.
    Ответ написан
  • Как разобраться, что происходит в этом заголовочном файле?

    @Mercury13
    Программист на «си с крестами» и не только
    Учи понятие «единица компиляции». Тут, к сожалению, есть и вещи, которые должны быть в CPP-файле, и вещи, которые должны быть в H-файле.

    #pragma pack(1)
    Структуры данных нам нужны «один в один», без байтов заполнения.

    struct FileHeader 
    struct MAPINFO

    Формат BMP. Не забывай, что формат BMP записывается с нижней строки!

    Функция Open читает картинку «один в один», Save пишет «один в один», GetMapInfo и GetFH выдают какие-то заголовки нашего BMP.

    Остаётся GetMap(), который, по идее, должен выдавать матрицу цветов, но реально действует только для 32-битного BMP и никак не инкапсулирует ни ширину-высоту матрицы, ни тот факт, что формат BMP пишется с нижней строки.

    За этот код — тройка с минусом.

    А теперь чего ваш код НЕ поддерживает, но, по идее, должен, чтобы выполнить вашу задачу.
    1. Создание BMP нужного размера с нуля, а не загрузка из файла.
    2. Инкапсулировать матрицу пикселей. Желательно так, чтобы был быстрый доступ к строкам как к буферам в памяти, для простоты переноса информации из старого BMP в новый, на 30×30 пикселей больший.
    3. Если вы ограниченно поддерживаете формат BMP — вылетать с ошибкой, если версия неподдерживаемая (например, не то количество цветов).

    Задача именно своими силами наладить поддержку BMP? А то в Builder’е есть TBitmap.
    Ответ написан
    6 комментариев
  • Как организовать указатели в си?

    @Mercury13
    Программист на «си с крестами» и не только
    Дело тут в очерёдности операций.
    Сначала вычисляются правые одноместные, потом левые.

    Надо (**h)[1] = 0;.
    Ответ написан
    Комментировать
  • Qt checkbox this combobox?

    @Mercury13
    Программист на «си с крестами» и не только
    Через сигналы-слоты способа не вижу.
    Унаследоваться от QStandardItemModel, присосаться на setModelData.
    А ещё лучше унаследоваться от QAbstractTableModel и переопределить все эти data(), flags()…
    Ответ написан
    Комментировать
  • Передача ссылки на экземпляр объекта из конструктора?

    @Mercury13
    Программист на «си с крестами» и не только
    Дело у вас вот в чём. Файл Part_Of_Word.h напоминает единицу компиляции (*.cpp), но имеет расширение заголовочного файла (*.h). После того, как вы удалили все его include’ы, эти функции просто перестали компилироваться.

    А до этого, вангую, у вас было два include’а — а значит, две копии одной функции из разных единиц компиляции. Тоже ошибка линкера.

    Решение: переименовать этот файл в cpp.
    Ответ написан
    Комментировать
  • Как понять: массив указателей на объекты классов?

    @Mercury13
    Программист на «си с крестами» и не только
    Вы тут просто не знаете, что такое указатель. А это, грубо говоря, адрес другого объекта в памяти.
    Операция new заводит объект в «куче», вызывает конструктор (если таковой есть) и возвращает указатель на него.
    И мы можем разыменовать указатель: пройти на тот объект, куда он указывает, и сделать с ним что-то. В Си разыменование — операция ->.

    Указатели не на абстрактный класс, а на вполне конкретный, a.

    a(int) — не конструктор копирования, а просто конструктор, делающий объект из int’а. Конструктор копирования всегда a(const a&). Отличается от других конструкторов тем, что автоматика его по возможности строит, но не всегда верно. В вашем объекте с одним полем int вполне себе построит без всяких проблем.

    Обычный указатель Си никоим образом не знает, есть объект или нет, и кто этот объект уничтожает. У вас это приводит к ошибке «утечка памяти»: теряется указатель на объект, и уже никак его не освободишь. Из-за небольшого размера и линейного характера программы неопасно, но всё-таки.
    Ответ написан
    Комментировать
  • Почему 150/1000 == 0?

    @Mercury13
    Программист на «си с крестами» и не только
    Потому что деление происходит int/int, то есть целочисленное.
    Надо 150.0/1000.

    Это известный глюк Си-подобных языков — a/b в зависимости от типа или деление, или неполное частное. В Паскале, например, неполное частное — a div b. В Си-подобных языках с динамической типизацией (JS) отказались от неполного частного, ибо мы тип плохо контролируем.
    Ответ написан
    8 комментариев
  • Как правильно передавать функцию как параметр C++?

    @Mercury13
    Программист на «си с крестами» и не только
    Не забывайте, что у startManufacturing есть скрытый параметр this.

    Четыре варианта.

    1. Использовать указатель на метод App:
    Rest::function(char * function_name, int (App::* f)(String), App& object)
          // вместо App можно какой-то интерфейс, который App реализует
    …
    object.*f("string");
    ...
    bt_rest.function("", &App::startManufacturing, *this);


    2. Сделать startManufacturing static:
    class App {
      static int startManufacturing(String command)
    };


    3. Сделать обёртку с замыканием:
    Rest::function(char * function_name, int (*f)(String, void*), void*);
    
    void doStartManufacturing(String command, void* closure) {
      reinterpret_cast<App*>(closure)->startManufacturing(command);
    }
    ...
    bt_rest.function("startManufacturing", doStartManufacturing, this);


    4. «Избегай незнакомых женщин и глобальных переменных». Костыль, в общем.
    App app;
    int doStartManufacturing(String command) { return app.startManufacturing(command); }
    ...
    bt_rest.function("startManufacturing", doStartManufacturing);


    Ах да. Вы передаёте String’и по значению. Приспособлены они к такой передаче или всё же лучше по ссылке?
    Ответ написан
    6 комментариев
  • Как нарисовать линию с помощью алгоритма Брезенхема и гамма-коррекции в текстовом файле?

    @Mercury13
    Программист на «си с крестами» и не только
    Я бы работал по такой формуле:

    result = ((original / max)1 / gamma) · 255

    Original — это полученное алгоритмом Ву значение (float или хотя бы short!)
    max — максимально возможное значение original. Для short, например, это 65535.
    gamma — традиционно 2,2.
    Ответ написан
    Комментировать
  • Как работает scanf в плюсах?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Для ввода строки использовать std::getline.
    2. Ну, в Си++ есть хорошо инкапсулированные строки, а в Си нет. Потому в Си очень сложно получить из потока строку неизвестной длины.
    Ответ написан
  • Почему не работает new c++?

    @Mercury13
    Программист на «си с крестами» и не только
    Есть такое понятие, как «принцип подстановки Барбары Лисков». Если подставить на место User класс Comparator, должно сойтись по типам. Но нет: User умеет сравнивать только с самим собой, а Comparator — с любым Comparator.

    Теоретически должно было бы подойти такое (так называемая контравариантность). Но не работает ни в Си++, ни в Яве.
    class ComparatorBase
    {
    public:
        virtual ~ComparatorBase() = default;
    };
    
    class Comparator : public ComparatorBase {
        public:
            virtual int compare(Comparator  *t)=0;
    };
    
    class User : public Comparator{
    public:
        int compare(ComparatorBase *u) override {
            return 1;
        }
    };

    А по возвращаемому значению (если возвращается ссылка/указатель) может быть ковариантность, и она работает и в Си++, и в Яве.
    class Father
    {
    public:
        virtual Father& foo();
        virtual ~Father() = default;
    };
    
    class Son : public Father
    {
    public:
        Son& foo() override;
    };


    Как обойти. Есть два способа.
    Способ 1. Пусть старой Явы.
    int User::compare(Comparator *u)
    {
        auto* v = dynamic_cast<User*>(u);
        if (!v)
            return false;
        return v->name > this->name;
    }


    Способ 2. Так называемый «странно рекуррентный шаблон».
    template <class T>
    class Comparator {
    public:
        virtual int compare(T *t)=0;
        virtual ~Comparator() = default;
    };
    
    class User : public Comparator<User> {
    private:
        std::string name;
    public:
        User(std::string name){this->name=name;}
        int compare(User *u);
        void showName(){std::cout<<"Name "<<this->name<<std::endl;}
    
    };
    
    int User::compare(User *u)
    {
        return u->name > this->name;
    }
    Ответ написан
    Комментировать
  • Какие есть способы перевода с одного типа данных в другой?

    @Mercury13
    Программист на «си с крестами» и не только
    1. С использованием функций преобразования типа, встроенных в Си++ или написанных пользователем. Это может делаться неявно, через C-style cast, через вызов конструктора, через операцию static_cast.
    2. С преобразованием указателя вниз по иерархии наследования, и особым поведением, если это не получилось, через операцию dynamic_cast и функцию dynamic_pointer_cast.
    3. С рассмотрением участка памяти как переменной несовместимого типа. Через union, const_cast, reinterpret_cast, C-style cast указателей.
    Ответ написан
    Комментировать
  • Как задать свойство класса экземпляром другого класса, если его значения не известны при инициализации?

    @Mercury13
    Программист на «си с крестами» и не только
    center_pt = *new Point(x/size, y/size);
    Утечка памяти. Никто в «си с крестами» за вас не будет подчищать все эти new.
    Правильно:
    center_pt = Point(x/size, y/size);
    (Ну и, разумеется, я не понимаю, что значат эти x/size и y/size, но шут с ним.)

    Примерно таким образом и происходит переприсваивание объектов, если им оставили таки операцию =. То есть…
    class Geolocator {
    public:
      Point coords;
      bool isReliable;
    
      Geolocator() : coords(0,0), isReliable(false) {}
    
      void getFromSensor() {
        coords = Point(100, 100);
        isReliable = true;
      }
    };


    Вариант 2. Через указатель, создание и уничтожение. Для простоты не буду это делать «руками», а воспользуюсь указателем единоличного владения unique_ptr.
    class Geolocator {
    public:
      std::unique_ptr<Point> coords;
    
      void getFromSensor() {
        Point pt(100, 100);
        if (coords) {
          *coords = pt;
        } else {
          coords.reset(new Point(pt));
        }
        // а если и операции = у Point нет, то можно
        // coords.reset(new Point(100, 100));
      }
    
      void declareUnreliable() {
        coords.reset();
      }
    };
    Ответ написан
    6 комментариев
  • Не заканчивается программа?

    @Mercury13
    Программист на «си с крестами» и не только
    Потому что конец консоли с клавиатуры зависит от ОС, и для Windows это Ctrl+Z.
    Нажатие ввода не передаёт в поток EOF.
    Ответ написан
    6 комментариев
  • Ввывод сообщения об успешном завершении программы в с++?

    @Mercury13
    Программист на «си с крестами» и не только
    Вопрос в буферизации потоков ввода-вывода. Разумеется, на Windows и OSX эти подробности устроены по-разному. Два модификатора, std::endl и std::flush, сбрасывают буфера.
    cout << "Кол-ва нат чис: " << std::flush;
        cin >> n;
        
        cout << sum(n) << std::endl;
    Ответ написан
    Комментировать
  • Можно ли число представить в виде float?

    @Mercury13
    Программист на «си с крестами» и не только
    Float содержит 23 бита мантиссы и неявную единицу. Порядок float запредельный и даже qword не упрётся в него.
    Это значит: всё, что дальше 23 бит от верхней единицы, должно быть нулём.

    bool isPreciseFloat(unsigned long long n)
    {
      // Простейшая проверка: стираем нижние 24 бита
      // Если в них вписываемся — ДА.
      unsigned long long n1 = n & ~((1ULL << 24) - 1);
      if (n1 == 0)
        return true;
    
      // Получаем верхнюю единицу
      // (можно также двоичным поиском, но я этого с листа не напишу)
      while (true) {
        unsigned long long n2 = n1 & (n1 - 1);
        if (n2 == 0)
          break;
        n1 = n2;
      }
    
      // Получаем маску всего, что ниже 23 бит от верхнего бита.
      n1 >>= 23;
      --n1;
    
      // Проверяем по маске
      return (n & n1) == 0;
    }


    Писал «с листа», могут быть ошибки.
    Ответ написан