Ответы пользователя по тегу C++
  • Как написать простой калькулятор?

    @majstar_Zubr
    C++, C#, gamedev
    Я отвечу для случая работы с простым форматом ввода:
    12345 + 6789

    1) вы не модифицируете входную строку, поэтому, если имеем дело со стандартом от c++17, то лучше в функцию принимать явно string_view или const string_view&, если ниже, то принимать надо const string&.

    2) если уж принимаем что-то строкоподобное, то смело пользуемся функцией-членом ::find, которая есть и в строке и в вью. С её помощью можно найти сразу позицию арифметического символа. Для простого случая, даже не нужен обход от найденной позиции к началу строки и к концу строки для проверок std::isspace, является ли символ пробелом, поскольку мы можем сразу слать в atoi для левого числа например string_view::substr(0, opPos - 1). И, получится, только один if/switch, который будет сопоставлять символ операции, например, с указателем на функцию.
    ...
    int plus(int left, int right) { return left + right; }
    
    using CalculatorFunction = int (*)(int, int);
    ...
    CalculatorFunction operation;
    ...
    switch (opChar):
    case '+': operation = plus;
    ...


    3) настоятельно рекомендую после этого реализовать нормальный простой калькулятор, а потом третьим заходом добавить поддержку скобок, hex oct бинарную и с мантиссой нотации записи чисел, операцию степени и корня.
    Нормальный - имеется ввиду, что в калькулятор входит строка, которая является корректным арифметическим выражением. В этом случае нужно ещё иметь дело с разделителями. По-хорошему, решение состоит из нескольких этапов:

    - на первом логическом этапе функция парсит выражение и сохраняет в контексте или возвращает контейнер с токенами, (или прерывает весь процесс из-за плохой строки). В вашем варианте, задача однопоточная, поэтому можно воспользоваться функцией strtok, чтобы удобно извлечь строковое значение токена по входной строке от разделителя до разделителя.

    - как вариант, токены могут быть структурами с членом string_view на строковые представления и членом на тип токена (число, операция).

    - на втором логическом этапе мы считываем последовательность токенов и делаем какое-то действие:
    • считали число - ожидаем за ним токен операции
    • если оказался не токен операции - бросаем исключение о некорректном арифметическом выражении, иначе - подготавливаем аргументы и кормим функцию evaluate<T>( Token::kOperation op, T arg1, T arg2) , для случая, если операции нужно два аргумента, то первый уже считан, а второй будет в последовательности токенов за операцией. Результат evalaute записываем в переменную результата, которая, кстати говоря, типа double.
    • продолжаем движение по последовательности токенов до конца, контейнером может быть std::list, когда добрались до конца, возвращаем результат.
    Ответ написан
    Комментировать
  • Какая IDE удобнее и проще для плюсов?

    @majstar_Zubr
    C++, C#, gamedev
    Это очень не умно, спрашивать у других о том, что для самого себя проще и удобнее. Особенно критично это для плюсов. Вот из чего я исхожу.

    C++ - свободный язык, если вы хотите произвести на свет новую платформу, и при этом хотите, чтобы уже написанные 100500 программных продуктов на нем работали, вы просто производите свой компилятор для C++. С Ide ситуация схожая.

    Что-то уровня десятков тысяч строк, комфортно можно написать в любой IDE. Но когда размер становится больше, или нужно использовать какой-то специфический пайплан разработки, только тогда становится понятно, зачем yet another IDE.

    На самом деле, все зависит от количества составляющих программного проекта, и как это все дело собирается.

    Qt Creator создана для удобства работы с Qt, особенно с точки зрения скрытия сложности сборки. Например, для VS лишь относительно недавно они родили на свет приемлемый плагин, который не заставляет добавлять файлы meta object compiler для каждой сборки проекта вручную. Но всё ещё осталось, мягко говоря, все остальное: WYSIWYG редакторы для файлов ресурсов, которые не всегда корректно работают, редактор для быстрого формошлёпства, удобная работа со старыми версиями qt, производительности плагина, и прочее и прочее. С qt работать из других qt стоит лишь тогда, если ваша система сборки не будет меняться чаще раза в N лет, иначе лишние траты на перенастройку сборки.

    Если что-то коммерческое собирается для Windows не на MSBuild + VC++, то могу пожелать счастливой отладки и напомнить, что спасение утопающих - дело рук самих утопающих.

    Eclipse нужен в случаях когда нельзя применять коммерческое ПО в ходе разработки. Или, если вы пишете про специфичную или открытую платформу, потому что как правило, в таких случаях владельцы платформы IDE не предоставляют по ряду причин. Тут на помощь приходит всемирно известный конструктор для IDE - Eclipse. Ещё его можно рассматривать в случаях, когда вы оптимизируете процесс разработки для себя. Хотя, этим заниматься лучше всего на Vim, Emacs. Насчёт последних - если кодовая база за миллионы строк, что выбора может не быть, но скорее всего, большинства а разработчиков эта проблема не касается.

    CLion нужен для тех, кто привык к другим IDEшкам от jetbrains. Хорошие, удобные, потому что коммерческие, постоянное развитие. На stepik можно решить тяжёлые задачи и получить лицензию на несколько месяцев, персональную. Хотя многие рефакторинги ещё не работают.

    Из ide ещё упоминают netbeans, а также многие редакторы, типа gedit, vs cide, geany и прочие.

    Если опыта разработки нет, то нужно начинать с geany. Потом переходить, например, на vs code. А уже потом пытаться что-то делать в IDE и делать проекты для сборки на разных платформах, например, с помощью cmake. Дело в том, что нужно знать, что делает при каждом чихе IDE, и самый органичный способ это сделать - наращивать сложность пайплацна разработки постепенно: сначала проекты на сотни строк, потом добавляем статический анализ, автоформатирование, тесты, git, управление сборкой и CI/CD. К тому же, это коррелирует с основным принципом C++ - "я не плачу, за то, что не использую", т.е. время на лишнюю возню из-за узкой специализации IDE, либо недостаточной специализации - оно не тратится. Именно исходя из этого, Евгений Шатунов вас спросил о вашей задаче, чтобы посоветовать что-то дельное.
    Ответ написан
    Комментировать
  • Можно ли заработать C++ программисту в 2021?

    @majstar_Zubr
    C++, C#, gamedev
    Можно! Для этого нужно всего лишь вступить в трудовые отношения. Для того, чтобы это сделать, нужно соответствовать ожиданиям работодателя.
    Опыт можно получить во время стажировки, во время ковыряния в опенсорсе, во время рефакторинга своих пет проектов. Если только сейчас начали, то через год можете начинать искать. Джуна никто учить не будет, устраиваясь на работу надо уметь выполнять задачи, код должен быть чистым и правильным вне зависимости от должности и иерархии в команде.
    Вакансий относительно не так много может быть, поэтому однозначно стоит откликаться на вакансии уровня мидл, вероятность, что ищут так же и джунов не нулевая.
    Ответ написан
  • Как лучше давать названия переменным?

    @majstar_Zubr
    C++, C#, gamedev
    Однообразно и согласовано с остальным кодом проекта.
    Ответ написан
    Комментировать
  • Не выводится масссив строк | наследование в С++ | Как исправить?

    @majstar_Zubr
    C++, C#, gamedev
    Дело не в наследовании, а в том, что вы не проинициализировали массив для объекта с с помощью вызова c.inputArray(). Реализуете конструктор копирования для удобства.

    Есть ещё некоторые критичные проблемы в вашем коде:
    1) у C2 нет деструктора с освобождением памяти.
    2) у C2 поля публичные, особенно плохо, что выставлен владеющий raw указатель.
    Ответ написан
    Комментировать
  • Как оптимизировать программу?

    @majstar_Zubr
    C++, C#, gamedev
    Вывод в stdout и жизнь программы суть разные вещи.
    Для ускорения вывода можно заменить std::endl на std::flush.
    Ответ написан
    Комментировать
  • Занимает ли переменная какую-нибудь память после ее объявления?

    @majstar_Zubr
    C++, C#, gamedev
    1) не иденцифированная, а не инициализированная.
    2) в данном случае, память выделяется на стэке.
    Ответ написан
    Комментировать
  • Самый легкий в изучении игровой движок для С++?

    @majstar_Zubr
    C++, C#, gamedev
    Только не путайте лёгкое и быстрое в использовании с лёгким и быстрым для понимания.

    Свой самописный движок - самый лёгкий в изучении, потому что там меньше кода и используемых концепций.

    На деле, у между 2d и 3d разница невелика на текущий момент, если только вы не собираетесь совершать паломничество и писать свой софтверный рендер, 2d проще только с точки зрения левел-дизайна.

    Cocos 2d creator - это не движок, а набор инструментов для полного workflow для разборки игры, с акцентом на ассеты. Документация по Cocos2-x - вот что должно вас интересовать в первую очередь.

    В общем случае, можно брать любой движок, у которого толстая документация и много примеров.
    Ответ написан
    Комментировать
  • Что происходит в этих строках кода?

    @majstar_Zubr
    C++, C#, gamedev
    ceh *p;                  // указатель типа ceh
    p = new ceh[2];.    // присваивание значения на куче
    p->set("Ivan", "moroz", 12); // обращение к функции-члену p[0]
    void (ceh::*pf)();    // указатель типа функции-члена структуры ceh
    pf = &ceh::show;. // присваивание значения указателю 
    (p[0].*pf)();  // вызов функции-члена по указателю для нулевого
    (p[1].*pf)();  // для первого, но возможно сначало надо для него через set установить значения полей
    Ответ написан
    Комментировать
  • Как понять вызов функци внутри другой функции с разным количеством параметров?

    @majstar_Zubr
    C++, C#, gamedev
    Короче говоря, первую функцию Assert нужно переименовать в AssertIsTrue, а параметр hint в errorMsg. Там должен быть текст, который будет в runtime_error объяснять, откуда взялось значение параметра b из функции Assert. Например, имя функции с аргументами или имя переменной.
    Ответ написан
  • Как заставить код выполниться лишь раз в почти бесконечном цикле?

    @majstar_Zubr
    C++, C#, gamedev
    void do_one_time() {
         static bool was_called;
         if (!was_called) {
              // ... полезный код
         }
         was_called = true;
    }
    Ответ написан
    Комментировать
  • Что такое экземпляр void?

    @majstar_Zubr
    C++, C#, gamedev
    #include <iostream>
    using namespace std;
    
    int main() {
    	cout << int() << endl;
    	cout << double(78) << endl;
    	
    	cout << bool(-1) << endl;
    	
    	// void r = void();
    	// error: void value not ignored as it ought to be
    	
    	void();
    	void(42);
    	return 0;
    }


    Вывод:
    0
    78
    1

    Это синтаксический сахар, который можно понимать, как конструктор встроенного типа, но фактически это работает, как приведение к типу

    (int)0, (double)76, (bool)-1 соответственно

    и вызова конструктора не происходит.

    Т.е. при компиляции вместо void() произойдет (void)0, результатом будет void. Функция foo возвращает void, потому что это "обратная совместимость" с языком C.
    Ответ написан
  • Как правильно перегрузить конструкторы в классе?

    @majstar_Zubr
    C++, C#, gamedev
    Рассмотрим проблему подробнее.

    class A {
    private:
     int aa;
     int ab;
    
    public:
     A()
         : aa(0), ab(0) {}  // (4) note: candidate constructor not viable: requires
                            // 0 arguments, but 1 was provided
     A(int a)
         : aa(a), ab(0) {}  // (2) note: candidate constructor not viable: no known
                            // conversion from 'A' to 'int' for 1st argument
     A(A& obj);  // (3) note: andidate constructor not viable: expects an l-value
                 // for 1st argument
     void Show() { cout << "Var1: " << aa << endl << "Var2: " << ab << endl; }
    };
    
    int main() {
     A obj = 5;  // (1) error: cannot bind non-const lvalue reference of type ‘A&’
                 // to an rvalue of type ‘A’
     obj.Show();
    
     return 0;
    }


    Стандарт обязывает понимать

    A(A& obj);

    как user-defined конструктор копирования. Поэтому, конструктор копирования T::T(const T&) не будет объявлен по-умолчанию компилятором. Со стандарта C++11 предоставляется возможность заставить компилятор генерировать неявно-объявленный конструктор копирования ключевым словом default

    A(A& obj) = default;

    Неявно объявленный конструктор копирования по-умолчанию имеет сигнатуру T::T(const T&) но лишь в случае, когда все базовые классы T имеют конструкторы копирования с параметрами const B& или const volatile B&, и когда все не статические данные-члены класса T имеют конструкторы копирования с параметрами const M& или const volatile M&.

    В ином случае, неявно объявленный конструктор копирования имеет сигнатуру

    T::T(T&)

    В данном случае, конструктор копирования A(A& obj) является тривиальным с сигнатурой сгенерированного неявно-определённого конструктора копирования T::T(T&).
    Тривиальное копирование практически аналогично std::memmove

    В строке ошибки компиляции происходит следующее:

    1) оператор = в строке с ошибкой компиляции не является инициализирующим, поскольку литерал 5 является (rvalue, нельзя взять адрес) const int. Для исполнения операции присваивания компилятор сначала конструирует A(5);

    ...
    A(int a) : aa(a), ab(0) {
     cout << "Copy ctor with 1 parameter is called " << endl;
    }
    …
    int main() {
    …


    Вывод:
    Copy ctor with 1 parameter is called
    Var1: 5
    Var2: 0


    2) операция присваивания имеет дело с A obj = A (5):

    Справа от оператора присваивания находится временный rvalue типа class A.
    Данное присваивание является инициализирующим, что делает его эквивалентным A obj(A(5));
    Для данной операции необходим конструктор с сигнатурой T::T(T&&)
    Это - конструктор перемещения, и он в классе A отсутствует, поскольку неявное определение конструктора перемещения в классе требует отсутствия user-defined конструкторов копирования, оператора = копирования, оператора = присваивания, деструктора. В нашем случае, у нас имеется user-defined A(A& obj);

    Учитывая вышесказанное, для исправления ошибки компиляции можно либо удалить строку
    A(A& obj); ,
    что приведёт к неявному определению компилятором тривиальных конструкторов копирования и перемещения, либо добавить ещё в объявление класса A строку
    A(A&& obj);

    С точки зрения стандарта С++11 и выше, можно утверждать, что выражения A(A& obj); и A(A&& obj); соответственно эквивалентны A(A& obj) = default; и A(A&& obj) = default;
    Ответ написан
    5 комментариев
  • Как разделить нажатие стрелок и нажатие русских букв?

    @majstar_Zubr
    C++, C#, gamedev
    Вам не нужна getch, не пользуйтесь ей.

    https://docs.microsoft.com/en-us/cpp/c-runtime-lib...

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

    Но всё уже придумано, попробуйте _getwchar
    https://docs.microsoft.com/en-us/cpp/c-runtime-lib...
    Ответ написан
    Комментировать
  • Prototype, правильно я понял, что он должен выглядеть примерно так?

    @majstar_Zubr
    C++, C#, gamedev
    Нет единого мнения по любому паттерну - так и должно быть, ведь они относятся к ООП, а не к конкретным языкам, имеющим свои идеомы.

    В целом - да, паттерн "встроен" в язык, т.к. можно определять конструктор копирования, но фактически функция-член clone() не нужна.
    Ответ написан
    Комментировать
  • Есть ли сборник примеров/советов, обучающих С++ программированию?

    @majstar_Zubr
    C++, C#, gamedev
    Лично меня учит исходный код, который нахожу на github и в исходниках boost. По-другому не знаю как ещё.
    Мне кажется , что каждый, кто пишет на C++ считает, что у него навыки ниже среднего, лично мне только такие люди попадались. С книгами все не однозначно, потому что в них много опечаток, и даже ошибок, что не удивительно, если речь идёт о C++. Даже стандарт C++ без ошибок в компиляторе поддержать задача не тривиальная.
    В общем, спасение утопающих - дело рук самих утопающих.
    https://github.com/fffaraz/awesome-cpp
    Ответ написан
    4 комментария
  • Какая из библиотек для прорисовки gui более производительна и менее требовательна?

    @majstar_Zubr
    C++, C#, gamedev
    особо стоит выделить https://github.com/ocornut/imgui

    и вообще: https://github.com/fffaraz/awesome-cpp#gui
    Ответ написан
    Комментировать
  • Правильны ли мои рассуждения в комментариях к коду?

    @majstar_Zubr
    C++, C#, gamedev
    У вас ошибка только в комментариях.
    Данное выражение new ничего не ищет, оно по сути пытается выделить память, сконструировать и инициализировать в этой памяти объект указанного типа. В данном случае будет выделено sizeof(int) байт. Причем выделение будет осуществлять void* operator new ( size_t count ), который после стандарта C++11 вообще может выкинуть std::bad_alloc exception, а конструирования и инициализации не произойдет, потому как int - фундаментальный целочисленный тип.

    Однако, современная практика написания кода на c++ подразумевает, что при использовании обычных указателей, каждое использование ключевого слова new сопровождается использованием ключевым словом delete в той же области видимости. Поэтому отсутствие delete aaa_num; считается ошибкой.
    Ответ написан
    2 комментария
  • Онлайн игры для программистов?

    @majstar_Zubr
    C++, C#, gamedev
    Друг мой делал, но там javascript, пока на стадии PoC duna-alt.com

    Есть игра типа clash of clans где нужно ии писать: https://empireofcode.com

    Конечно есть ещё https://screeps.com , но все слишком абстрактно.
    Ответ написан
    Комментировать
  • Как именовать булевские "флаги"?

    @majstar_Zubr
    C++, C#, gamedev
    Поскольку в любом случае придется лезть в чужой код и обрамлять там что-то, то единообразный подход уменьшит общую сложность восприятия кода.

    С психологической точки зрения проблема в том, что вы не хотите брать ответственность за "чужой" код. Как только вы позволите себе взять ответственность за т.н. "чужой" код, потому что он сейчас вовсе не чужой, он ваш т.к. он под вашей опекой, вы взялись за его модификацию, ваше подсознание не будет противится и в результате у вас будет меньше склонностей к внедрению в код лишних сущностей, и в целом работа пойдет быстрее. ( Если переформулировать ваше предложение: вы предлагали вынести на функциональный уровень управления фичами разделение кода в зависимости от просихождения кода. )
    Ответ написан