Ответы пользователя по тегу C++
  • Есть ли фриланс на С++ с нуля?

    TrueBers
    @TrueBers
    Гуглю за еду
    C++ -- инструмент, а не цель.

    "Если у меня есть молоток, то какие типы гвоздей мне им забивать?"

    На C++ можно писать ОС, игры, прикладные программы, системные утилиты, драйверы, прошивки, серверы, базы данных, да хоть чёрта лысого.
    Что интересно самому, тем и занимайся. C++ тут не причём, это 10% от всего, что нужно изучать.

    Большинство заказчиков на фрилансе вообще не шарят за языки и технологии. Им важно, чтобы задача была выполнена, а на чём ты её напишешь, большинству обычно всё равно.
    Ответ написан
    2 комментария
  • Как исправить ошибку LNK2001 unresolved external symbol __imp_GetUserNameW C++?

    TrueBers
    @TrueBers
    Гуглю за еду
    Гуглим название функции: GetUserNameW.
    Заходим по первой же ссылке на документацию Microsoft.
    Там спускаемся в раздел Requirements.
    В строке "Library" видим Advapi32.lib.

    Теперь гуглим текст ошибки LNK2001 unresolved external symbol и переходим по первой ссылке, которая снова, внезапно, по невероятному стечению обстоятельств оказывается документацией Microsoft.
    Там читаем описание ошибки, и что нужно сделать, чтобы её устранить. Там даже ссылка на StackOverflow оставлена!

    2 основных варианта: либо в настройках проекта добавляем эту библиотеку к списку линкуемых, либо прямо в коде говорим линкеру #pragma comment(lib, "Advapi32").
    Браво! Вы великолепны!
    Да ещё и потратили в 3 раза меньше времени, чем писать сюда вопрос.

    Не сложно, если захотеть, правда?
    Ответ написан
    Комментировать
  • MSVCP140.dll, что делать sfml?

    TrueBers
    @TrueBers
    Гуглю за еду
    6440f5fb8784f355684811.png
    Ответ написан
  • Как можно перебирать слова в C++?

    TrueBers
    @TrueBers
    Гуглю за еду
    Обычно, слова в коде Морзе в рукописном варианте делят не пробелами, а прямым слешем: "/". А если в кодировке, то 7-ю длительностями низкого уровня.

    Словарь на такое мизерное количество ключей будет иметь на порядок больше оверхеда, чем простой линейный поиск в массиве:

    C++23
    #include <string>
    #include <string_view>
    #include <ranges>
    #include <array>
    #include <utility>
    #include <algorithm>
    #include <cassert>
    #include <stdexcept>
    
    template <typename Key, typename Value, std::size_t Size>
    struct Map {
    	std::array<std::pair<Key, Value>, Size> data;
    
    	constexpr Value at(const Key key) const {
    		const auto iter = std::find_if(std::begin(data), std::end(data),
    			[&key](const auto& v) { return v.first == key; });
    
    		if (iter == std::end(data)) {
    			throw std::range_error("unknown character");
    		}
    		return iter->second;
    	}
    };
    
    static constexpr std::array<std::pair<std::string_view, char>, 51> MORSE_MAP = { {
    	{"•-", 'A'},      {"-•••", 'B'},    {"-•-•", 'C'},   {"-••", 'D'},
    	{"•", 'E'},       {"••-•", 'F'},    {"--•", 'G'},    {"••••", 'H'},
    	{"••", 'I'},      {"•---", 'J'},    {"-•-", 'K'},    {"•-••", 'L'},
    	{"--", 'M'},      {"-•", 'N'},      {"---", 'O'},    {"•--•", 'P'},
    	{"--•-", 'Q'},    {"•-•", 'R'},     {"•••", 'S'},    {"-", 'T'},
    	{"••-", 'U'},     {"•••-", 'V'},    {"•--", 'W'},    {"-••-", 'X'},
    	{"-•--", 'Y'},    {"--••", 'Z'},    {"-----", '0'},  {"•----", '1'},
    	{"••---", '2'},   {"•••--", '3'},   {"••••-", '4'},  {"•••••", '5'},
    	{"-••••", '6'},   {"--•••", '7'},   {"---••", '8'},  {"----•", '9'},
    	{"•-•-•-", '.'},  {"--••---", ','}, {"---•••", ':'}, {"••--••", '?'},
    	{"•----•", '\''}, {"-••••-", '-'},  {"-••-•", '/'},  {"-•--•", '('},
    	{"-•--•-", ')'},  {"•-••-•", '"'},  {"-•••-", '='},  {"•-•-•", '+'},
    	{"-••-", 'x'},    {"•--•-•", '@'},  {"/", ' '} } };
    
    constexpr auto decode(std::string_view morse) {
    	constexpr auto map = Map<std::string_view, char, MORSE_MAP.size()>{ MORSE_MAP };
    	return morse
    		| std::ranges::views::split(' ')
    		| std::ranges::views::transform([](auto&& range) {
    			return map.at(std::string_view{ range });
    		})
    		| std::ranges::to<std::string>();
    }
    
    int main() {
    	const auto CODE = "•••• • •-•• •-•• --- --••--- / •-- --- •-• •-•• -••";
    	const auto TEXT = "HELLO, WORLD";
    
    	assert(decode(CODE) == TEXT);
    
    	return 0;
    }
    Ответ написан
    Комментировать
  • Как убрать предупреждение "F может быть равно NULL"?

    TrueBers
    @TrueBers
    Гуглю за еду
    Как минимум, не мешать Си и Си++, а писать на чём-то одном, и различать их.
    Во-вторых, зачем от предупреждения избавляться? Его нужно понять, почему оно появляется. Когда будет понимание, тогда всё станет на свои места.

    Достаточно предположить, что функция fopen_s внезапно не сможет открыть файл и вернёт ошибку. Допустим, файла не существует, или на его открытие нет прав у пользователя. Ты же не проверяешь возврат результата из функции открытия и пытаешься что-то записать в F, даже если функция не смогла открыть файл и вернула ошибку. А когда она вернёт её, переменная F останется нулевыем указателем или, в первом твоём случае, вообще будет обращение к неинициализированной переменной, где ты получишь неопределённое поведение. Вот оно и ругается на эту ветку развития событий.

    Добавь проверки на возвращаемые значения функций, и предупреждение пропадёт.
    Ответ написан
    Комментировать
  • Как в C++ создать процесс с именем Load.txt?

    TrueBers
    @TrueBers
    Гуглю за еду
    В стандарте С++ нет понятния процесса.
    Ответ написан
    Комментировать
  • Linux для изучения C++ новичку?

    TrueBers
    @TrueBers
    Гуглю за еду
    Современный С++ это ОЧЕНЬ много времени на изучение. Будет уходить ВСЁ свободное время. С++ ОГРОМЕН. Изучать что-то ещё параллельно не будет ни минуты, брось эту идею.

    Другое дело, если хочешь изучать современный стандарт С++20, то нужна последняя версия компилятора. А такую редко где найдёшь в Линуксах. В убунте слишком старый компилятор будет. В Федоре более-менее свежий. Самые свежие в Арче, но он для новичка очень сложен даже в установке.

    А вообще, можно не делать мозги, скачать под винду Visual Studio 2019 Community Edition, поставить в настройках проекта /std:c++latest и кодить себе спокойно. Это лучший, быстрый и самый беспроблемный вариант.
    Ответ написан
    Комментировать
  • Почему в C++ нужно строить всю программу на ООП (длинный вопрос)?

    TrueBers
    @TrueBers
    Гуглю за еду
    Длинному вопросу — длинный ответ:

    при этом под полиморфизмом понимается динамический полиморфизм

    А почему вы отрицаете статический? У динамического, например, есть накладные расходы на vtable, инвалидации кешей, и т. п. Если бы STL использовал динамический полиморфизм, у нас бы не было такой эффективности. А у статического таких проблем нет.

    инкапсуляция (то есть в C++ это будет выражено классом с private и public функциями-членами)

    Инкапсуляция — это совсем не про private и public. А скорее про интерфейс и его сокрытую реализацию.
    www.ddj.com/cpp/184401197

    Про отрисовку виджетов, на самом деле, вопрос холиварный. С виджетами изначально мы имеем поломанный интерфейс. Т. е. Widget может добавить в себя тип Widget* или Widget&. А теперь делаем так:
    class Widget {
    public:
      void add(shared_ptr<Widget> child);
    };
    
    auto root = make_shared<Widget>();
    root->add(root); // о_О шта?

    Мы так сделать можем, а значит интерфейс неверный. Кучи проверок в функции add() никому не нужны. Есть такой мужик Sean Parent, может слышали, вот он предложил этой проблеме замену на другую структуру данных со статическим полиморфизмом, без ненужных оверхедов. Они это используют в Фотошопе. На каком ещё языке это можно сделать столь эффективно?

    мы не знаем в данном месте кода, какой это именно виджет, но нам нужно вызвать draw, который может быть разным в зависимости от типа виджета

    А как же мы у STL-контейнеров тогда это вызываем и всё прекрасно работает без наследования и виртуальных функций?

    Вы скажете, что virtual call быстрее switch

    Не факт, зависит от оптимизатора и количества функций. Адекватный оптимизатор разворачивает свитч в jump-table, который на пару процентов может быть быстрее, чем виртуальный вызов. При этом, точно так же, он может девиртуализировать виртуальную функцию если она используется в элементарном базовом блоке, например, в цикле.

    От vector'а не рекомендуется наследоваться

    От него и никогда не требуется этого. Достаточно набросать адаптер, идеология C++ в трёх, как вы сказали, китах: контейнеры, итераторы и алгоритмы. Написав адаптер, он будет работать со всей стандартной библиотекой и алгоритмами, чего обычно более, чем достаточно. Например, те же std::queue, std::priority_queue и std::stack являются адаптерами и могут изменять вектор (и не только), ни от кого при этом не наследуясь.

    Я не вижу тут необходимости в классах. Той редкой ситуации, как было с теми виджетами, когда у нас есть указатель непонятно на что, тут нет. Так что наследование и полиморфизм тут не нужны

    Ну, дык, верно. Вас собеседуют же не всегда гении. Бывают дурни, задроты, идеалисты, холиварщики, ЧВСшники. Ничего хорошего от них обычно не стоит ждать. От таких надо валить подальше просто.

    Насчёт виртуальных функций в Си и прочих притаскиваний функций плюсов в этот язык. Считаю, что это дурость, когда можно просто создать coding convention для плюсового кода, и писать на нём в стиле Си, но при этом юзать множество полезных плюсовых фич. Это верный подход, имхо. А когда начинается имитация этого всего: забыл инициализировать, или где-то, непойми где, инициализировал не тем, пока найдёшь проблему, вместо того, чтоб просто открыть код конструктора.

    Итого, мой ответ:
    1. Своей задаче — свой инструмент. Не забивайте гвозди микроскопом, он вам пригодится целым для другого.
    2. Не холиварьте. Если так "положено", но нихрена ни в какие рамки не лезет, не нужно это дальше туда пихать. Нужно переступить через гордость и идеалы, и сделать по-другому.
    3. Выжимайте максимум из каждой парадигмы и технологии. Благо, в C++ этого навалом. Где-то функциональщина больше попрёт, где-то лучше шаблонами нагородить. А уж где не получилось, придётся костыли пихать.
    Ответ написан
    2 комментария
  • Как заменить символы в текстовом файле?

    TrueBers
    @TrueBers
    Гуглю за еду
    Подскажите как это лучше всего сделать с использованием только стандартных библиотек C++?

    Если прям true C++ way, то вам нужно использовать std::fstream. Для него нужно реализовать стандартный std::streambuf и во время записи в файл, когда буфер сбрасывается, у std::streambuf вызывается виртуальный метод overflow(), вот его перегружаете, проходите по буферу, делаете нужные вам изменения и сбрасываете буфер дальше на запись. Примерно, как тут.
    Это если важна архитектура, гибкость, переиспользование кода и трушность C++.

    А если более-менее простой вариант, то недо-true C++ way выглядит
    примерно так
    #include <algorithm>
    #include <fstream>
    #include <iostream>
    #include <iterator>
    
    int main() {
    
      std::ofstream out_file( "out.txt" );
      std::ifstream in_file( "in.txt" );
      std::string/*stream*/ some_buffer;
    
      using char_type = decltype(out_file)::char_type;
      using in_iter = std::istream_iterator< char_type >;
    
      if ( !in_file.is_open() || !out_file.is_open() )
        return EXIT_FAILURE;
    
      std::transform(in_iter(in_file), in_iter(), std::ostream_iterator< char_type >(out_file),
                     [&some_buffer] (const char_type &c) {
    
                       if ( !std::isalpha( static_cast<unsigned char>(c) ) || c == '\n' || c == '\r' )
                         return ' ';
    
                       // if шило, then мыло, буферизуем если надо, и т. п.
    
                       return c;
                     });
    
      return EXIT_SUCCESS;
    }
    Ответ написан
    3 комментария
  • Поиск слов в векторе c++?

    TrueBers
    @TrueBers
    Гуглю за еду
    Ответ написан
    Комментировать
  • Почему нельзя использовать указатель без объявления переменной?

    TrueBers
    @TrueBers
    Гуглю за еду
    А потому что, учите ассемблер, вот почему.
    Разработчика на C/C++ не бывает без базовых знаний ассемблера.
    Можно взглянуть сюда и понять очень многое.

    На 6-й строке ассемблерного кода у вас происходит выделение стекового фрейма. Это та область памяти, где будут храниться ваши локальные переменные, которые вы объявляете в текущей функции, т. е. в нашем случае это int var; int *pvar;. Несмотря на то, что сумма размеров этих переменных на стеке sizeof(int) + sizeof(int*) = 12 для x86_64, выделяется 16 байт согласно пункту 3.2.2 SystemV ABI AMD64, который говорит, что стековый фрейм должен быть выровнен по границе 16 байт. На другой архитектуре здесь может быть другое правило.

    Стек растёт вниз по адресам, т. е. выделение памяти на нём — это просто вычитание (инструкция SUB) из адреса его вершины (регистр RSP) числа, равного тому количеству памяти, которое мы хотим выделить. К вам как бы «всплывает» кусок памяти «откуда-то снизу». Эта память скорее всего использовалась предыдущими вызовами функций и осталась не затёрта! Соответственно, вы должны инициализировать этот поюзаный кем-то кусок под себя.
    Что-то типа аналогии со школы или универа, когда в аудитории есть такая раздвигающаяся доска из двух или трёх частей, видели наверное, когда выдвигаешь одну часть, а другая задвигается. Так вот, когда вы приходите на лекцию, выдвигаете доску, а там, внезапно, предыдущая группа не стёрла за собой свои творения, и вам придётся это стирать и писать вещи, относящиеся к вашему предмету. Естественно, если вы попытаетесь использовать для вашего предмета то, что осталось от предыдущей группы, получится бред в большинстве случаев.

    Когда адрес вашего указателя где-то используется без инициализации (7-я строка асм кода), он возьмёт тот самый мусор, который к нам пришёл при выделении фрейма.
    Получается лотерея: если этот мусор случайно оказывается действующим указателем на память, доступную для записи, ОС не выбрасывает исключение и пишет куда-то в непонятное место, ломая тем самым случайное место в вашей программе. Т. е. вы попадаете на тот случай, когда во фрейме попался до сих пор действующий указатель из мусора предыдущей функции, которая использовала этот кусок фрейма и не затёрла. А когда лотерея проиграна, и попали не на указатель, а на не пойми что, ОС бросает исключение. Поэтому ошибка не всегда проявляется.

    Это всё частный случай реализации языка Си для конкретной архитектуры. Конечно, в терминах стандарта таких вещей не бывает вообще, а объявлено просто как неопределённое поведение, которого нужно избегать.
    Ответ написан
    1 комментарий
  • Что означает этот кусок кода?

    TrueBers
    @TrueBers
    Гуглю за еду
    По уму это делается так:
    #include <ctype.h>
    
    if( isdigit( c ) )
      ++ndigit[c - '0'];
    else if( isspace( c ) )
      ++nwhite;
    Ответ написан
    2 комментария
  • C++. Как происходит возвращение локального объекта функциями?

    TrueBers
    @TrueBers
    Гуглю за еду
    samp *ob;
        ob = &func();


    Да, верно вы предполагаете, вы получаете адрес на временный объект, который будет уничтожен после выполнения деструктора и указатель будет указывать на мусор в стеке. ОС ошибку вам и не выдаст, это не её дело, что вы пишете неправильный код, это дело статического анализатора или адекватного компилятора.

    Включите все уровни предупреждений в вашем компиляторе и он вам скажет, что вы неправильно делаете. По крайней мере clang и gcc об этом скажут по умолчанию. Думаю, что и вижуал студия справится.
    Ответ написан
    Комментировать
  • С или modernC++ для IoT?

    TrueBers
    @TrueBers
    Гуглю за еду
    Так говорите, как будто С++ это 2 вещи: shared_ptr и исключения :D

    Вы смутно понимаете, похоже, в чём их назначение.

    Эксепшены и атомики в плюсах использовались всегда. Просто, нужно понимать, для чего они вам.
    Исключения, на то они и исключения, что используются в исключительных ситуациях. Если вы их пихаете везде, и жить без них не можете, значит, с вашей архитектурой приложения что-то не так. Это раз.

    Два. Любые эксепшены, rtti, наследования, диспетчеризация и т. п. в embed'е это дикий оверхед. Но при этом, почему-то многие софтовые компании, включая гугл, просто выключают у себя в проектах поддержку исключений и rtti. И на этом "огрызке" всё работает почему-то. К тому же, размер бинарника для имбеда довольно важен и не на последнем месте. Выпилив исключения и rtti, можно очень хорошо выкинуть мусор.

    shared_ptr — не серебряная пуля. Как уже сказал fshp, они используются в, ну просто, очень ограниченном количестве случаев. Их, как и исключения, нельзя пихать куда попало. Посмотрите выступления Шона Парента, почитайте его пейперы. Очень толковый мужик из Adobe, один из главных инженеров Фотошопа. Он там хорошо описывает и показывает на примерах, что shared_ptr — это просто хорошо замаскированная глобальная переменная, по сути, да ещё и с оверхедом от атомиков. И перед тем, как его использовать, сто раз подумайте, зачем он вам здесь нужен. В 99% случаев, можно обойтись без него, так же как и без RTTI. При этом, unique_ptr — очень удобная и полезная штука.

    Вам современный C++ предлагает множество сишных способов возврата значения вместо эксепшенов. Вы можете возвращать тот же tied tuple, optional, variant, any, error_condition, error_code. Хоть они и сами любят повыбрасывать исключения, но код намного чище и легче отлаживать и понимать, чем миллион вложенных исключений с дикой иерархией и оверхедом.

    А уж за что следует использовать плюсы, так это за мощь алгоритмов, итераторов, трейтов, лямбд, семантики переноса, удобных атомиков там, где они действительно нужны. Ну и, конечно, шаблоны 17-го стандарта могут хоть чёрта лысого в компайл тайме запилить.

    Если вы из всего вышеперечисленного, пользуетесь только shared_ptr и исключениями, то C++ вам не нужен.
    Ответ написан
    1 комментарий
  • Как использовать make?

    TrueBers
    @TrueBers
    Гуглю за еду
    Там же один файл в исходниках. Просто запустите компиляцию руками. Там какой-то древний Makefile.
    $ gcc -march=native -O2 -lz afsctool.c -o afsctool
    Ответ написан
    Комментировать
  • Объясните: какую базу нужно иметь для изучения Dirext X [C++]?

    TrueBers
    @TrueBers
    Гуглю за еду
    Вам нужны знания графики. Понимания, как оно всё там работает под капотом. Без этого всего DirectX будет казаться чем-то космическим и непонятным. DirectX никак не завязан на чистый WinAPI. Он базируется на технологии COM. Это посложнее для понимания, чем WinAPI. Ну и, соответственно, это всё работает только на Windows.
    OpenGL чуть проще, там чистое Сишное API. Можно писать для множества операционных систем, в том числе, для мобильных платформ.

    Оба API используют конечный автомат для рендеринга, все состояния которого тоже нужно отлично понимать. У OpenGL, на мой взгляд, он более лёгок для понимания.
    Но для всего этого вам нужно знать, что используется там под капотом. Бездумно искать строки кода и копипастить их к себе не получится. Нужно знать что делает каждая функция, какие состояния конечного автомата меняют, и т. п.

    Если хотите начать учить всё это дело, советую начать с отличных статей на Хабре. Если вы осилите весь этот курс, то сможете писать хоть на OpenGL, хоть на DirectX, хоть на Unity/Unreal/CryEngine.
    Ответ написан
    Комментировать
  • C++ fstream. Не работает чтение из бинарного файла, почему?

    TrueBers
    @TrueBers
    Гуглю за еду
    Т.е по сути моя i должна быть равна 3
    Не должна она вам ничего, пока документацию не прочитаете.

    Зачем вы добавили ios::trunc при открытии файла?
    trunc от слова truncate, переводится как: обрезать, усекать, урезать, сокращать.

    В документации ясно написано же: trunc discard the contents of the stream when opening. Что переводится как, отбрасывает содержимое потока при открытии.
    Он же вам его очищает и открывает новый пустой файл, вы его читаете, а там нечего читать. Вот и результат.
    Ответ написан
    1 комментарий
  • Какая хорошая книга для начинающего программиста на С/C++ с уклном в сети?

    TrueBers
    @TrueBers
    Гуглю за еду
    Сети — это сети. А C++ — это C++. Не бывает такого, чтобы была книга сразу по двум громадным направлениям.

    Сначала учите C++, а потом сети.
    К тому же, под C++ нет сетевых API. Они все всё равно написаны изначально на чистом Cи и никак не пересекаются.

    Изучив понятие сетей, можно писать на любом языке.
    Ответ написан
    9 комментариев
  • Возможно ли вывести элементы list используя for_each?

    TrueBers
    @TrueBers
    Гуглю за еду
    Откройте документацию хотя бы, и прочитайте про входные параметры for_each:
    template< class InputIt, class UnaryFunction >
    UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );

    От вас требуют на вход итератор на первый элемент first и итератор на последний элемент last. Ниже можно найти, что InputIt значит InputIterator.

    Теперь смотрим документацию std::list.
    Iterators
    begin   returns an iterator to the beginning 
    cbegin  (public member function)
    
    end     returns an iterator to the end 
    cend    (public member function)

    То, что нам нужно. begin() даст нам итератор на первый элемент, т.е. first, а end() — на last.

    Смотрим дальше
    Member types
    Member type	Definition
    
    iterator	BidirectionalIterator

    Смотрим, что такое BidirectionalIterator. Написано, что он должен быть в свою очередь ForwardIterator'ом. А ForwardIterator удовлетворяет концепту InputIterator. Как раз то, что нам нужно.

    В итоге, достаточно написать for_each(test.begin(), test.end(), show);

    Но, сейчас на дворе 2017 год, давно уже введены новые фишки в C++. Например, можно сходу передать анонимную функцию (лямбду) вместо обычной функции, в случае, если она однострочная:
    for_each(test.cbegin(), test.cend(), [](int v) {std::cout << v << std::endl;});

    Также, C++11 добавляет новые методы для получения константного итератора cbegin() и cend(), т. к. нам не нужно модифицировать список, лучше воспользоваться константными итераторами, чтобы избежать ошибок в случае случайной модификации.

    Либо, как уже сказали, если у вас слишком большое тело цикла, чтобы не городить из лямбд спагетти и чтобы это всё легко читалось, можно использовать range-for:
    for(const auto &v : test)
      std::cout << n << ' ';
    std::cout << '\n';
    Ответ написан
    Комментировать
  • Как создать кольцевой список на основе list?

    TrueBers
    @TrueBers
    Гуглю за еду
    Достаточно перегрузить итератор, закольцовывая его на начало при достижении конца, и наоборот. Вот и весь кольцевой список.
    Ответ написан
    Комментировать