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

    @Mercury13
    Программист на «си с крестами» и не только
    Пока вижу вот что.
    1. a += buff[i]; Вы суммируете коды символов, а не их числовые значения. Правильно a += buff[i] - '0';

    2. Эти строки дублируют друг друга.
    fin >> buff;
      fin.getline(buff, 11);

    Сначала получаем 0123456789, потом файл кончился и на место 0 записываем нулевой символ (NUL).

    3. for (int i = 0; i < 11; i++) — неуниверсальная конструкция. Плюс цифр в строчке всего 10, поэтому надо i < 10.

    4. Так сумму чисел или сумму цифр?
    Ответ написан
  • Как реализовывается функцию(main), которая будет производить проверку?

    @Mercury13
    Программист на «си с крестами» и не только
    Мы пишем модульный тест собственными силами, без фреймворка. Это значит, что нам надо каким-то образом сымитировать работу фреймворка, но собственными силами и минимумом строчек.

    Для удобства нам потребуется одна функция — и немного препроцессорной магии.
    #include <iostream>
    #include <cstring>
    
    void doAssert(bool condition, int line)
    {
        if (condition) {
            std::cout << "Line " << line << " ok" << std::endl;
        } else {
            std::cout << "Line " << line << " FAILED" << std::endl;
        }
    }
    
    #define ASSERT(x) doAssert(x, __LINE__)
    #define ASSERT_STREQ(x, y) doAssert((std::strcmp(x, y) == 0), __LINE__)


    А теперь что-нибудь протестируем. Например, создание строки. Для простоты я проверю не вашу строку, а std::string.
    void testStringCreation()
    {
        std::string s;
        ASSERT(s.length() == 0);
        ASSERT(s.empty());
        ASSERT_STREQ("", s.c_str());
    }

    Проверим ещё операцию +=.
    void testPlusEq()
    {
        std::string s1 = "Alpha";
        std::string s2 = "Bravo";
        s1 += s2;
        ASSERT_STREQ("AlphaBravo", s1.c_str());
    }
    
    int main()
    {
        testStringCreation();
        testPlusEq();
        return 0;
    }

    Нужны ещё тесты — создавай новую функцию и вызывай её из main.
    Если какой-то тест не проходит — уничтожаем из main все вызовы, кроме несработавшего, и начинаем отладку.

    А теперь немного о том, какие должны быть модульные тесты.
    1. Изолированные. Если уничтожить часть тестов или запустить их в другом порядке, ничего не должно меняться — при условии, конечно, что никто не обращается к чужой памяти.
    2. Каждый модульный тест проверяет одну концепцию, которая указана в его названии. Например: строка создаётся, работает операция +=, и т.д. Будет очень много дублей кода, просто смиритесь с этим.
    3. Но нет ничего зазорного, что какой-то тест полагается на концепции, испытанные в других тестах. Если мы испытываем +=, то полагаем, что конструктор копирования работает, и не будем испытывать это.
    4. Модульным тестам иногда приходится иметь дело с внутренним поведением объекта. В таком случае объект должен выдавать наружу некоторую информацию — например, соответствующие функции обычно private, но по какой-то препроцессорной команде они становятся public.
    5. Модульные тесты обычно имеют дело с простейшим поведением объекта. Это не нагрузочные тесты, надо с этим просто смириться.
    6. Принцип работы теста таков. Создать исходные объекты, убедиться в том, что их ВНУТРЕННЕЕ (неспецифицированное, зависящее от реализации) состояние верное, провести некое действие, убедиться, что действие проведено правильно. Например, пусть строка выделяет память с запасом, и надо проверить, как она расширяется — тогда при присваивании "Alpha" мы убеждаемся, что выделено менее 10 символов, затем проводим s1 += "Bravo", затем убеждаемся, что выделено, например, 16 символов.
    7. Но заранее не нужно проверять, верно ли ВНЕШНЕЕ (заспецифицированное) состояние. Если мы пишем s1 = "Alpha", значит, строка равна "Alpha", и точка. Все случаи, возможные в операции string = char*, разумеется, покрыты другими тестами.
    8. Если вдруг в «боевом» коде обнаружится ошибка, надо сделать юнит-тест, её повторяющий, затем исправить ошибку и констатировать, что тест проходит и остальные тесты не «упали».
    9. То же самое, если ошибка случилась, когда проверяли другую концепцию. Проверяем +=, а глюк в конструкторе копирования — заводи новый тест, покрывающий эту ошибку.
    10. В проверках на равенство принят порядок ASSERT_EQ(что_должно_быть, что_реально_есть).
    Ответ написан
    Комментировать
  • На какой версии C++ Builder(максим. доступной) можно запустить проект C++ Builder 6?

    @Mercury13
    Программист на «си с крестами» и не только
    Переписывать придётся.

    1. Потому что в паскале — string и есть string, и раньше он был Ansi-, а теперь Unicode-. А на Си — именно что AnsiString/UnicodeString.
    2. То же самое касательно char. В Паскале-то можно объявить, что отныне char двухбайтовый, а на Си — есть стандарт, и по стандарту Си char — это один байт.
    3. Алгоритмы-то какие используются? Возможны какие-то алгоритмические уловки на манер алгоритма поиска подстроки Бойера-Мура — там тоже придётся переписывать.
    4. Если программа работает с файлами или сетью — надо указывать, как юникодные строки сериализовать. На Windows распространены UTF-16 Intel и UTF-8. На этот счёт AnsiString почти не используется в истинно юникодных программах из-за того, что хранит кодировку; если нужна цепочка байтов — есть RawByteString или std::string.
    5. Иногда старые программы работают с Юникодом через WideString (обёртку над BSTR). Сейчас этот WideString придётся массово менять на Unicode-.
    6. По-другому пишутся стандартные хедеры Delphi/VCL. Ранее #include <Forms.hpp>, сейчас — #include <Vcl.Forms.hpp>.

    Версия Builder — ну, любая, какую найдёшь. Не забудь только, что из C++11 есть enum class (не совсем стандартный, правда) и кое-что ещё. Я не проверял, как работает новый clang под XE10, но это только вопрос эффективности, «въедливости» и C++11 — миграцию на Юникод придётся проводить, как и раньше.

    Насчёт «въедливости». Я всегда считал, что G++ — самый «въедливый» компилятор. Но по статическому анализатору clang (встроен в Qt Creator, мой сейчас рабочий инструмент) кажется, что он переплюнул и этот рекорд. И сделать, чтобы один и тот же файл компилировался под древним Borland и суперсовременным clang — тяжеловато, по-моему. А именно это требовал Embarcadero от XE3 до XE8, если нужны версии и под x86, и под x64. Так что пусть BCC32 идёт туда, куда ему и дорога!

    Да, приходилось мигрировать на Юникод, когда появился Builder XE. Как я был рад!

    Знаю, что подхватит и сконвертирует XE2. Про более новые — не знаю ничего. Но даже если не сконвертирует — ничего не стоит построить новый проект с нуля и добавить туда все модули и формы. Но сконвертировать — это лишь половина вопроса; дальше придётся добиться компилируемости, а потом — и Юникода.
    Ответ написан
    Комментировать
  • Как решить проблему вывода из потока?

    @Mercury13
    Программист на «си с крестами» и не только
    #include <iostream>;
    Это не синтаксис Си, а директива препроцессора. Убери точку с запятой.

    #include <conio.h>;
    conio.h — это не стандартный хедер, а Windows-специфичный. Разумеется, вместо _getch() придётся использовать что-то другое.
    Ответ написан
    Комментировать
  • Корректно ли такое сравнение вещественного числа с нулём?

    @Mercury13
    Программист на «си с крестами» и не только
    Так сравнивать безопасно. Единственный вопрос — может ли появиться ноль во время вычислений и не сломает ли это логику программы?
    Лучше, конечно, для этого использовать не 0.0, а 0.0f.
    Ответ написан
    2 комментария
  • В чем ошибка в операторе присвоения?

    @Mercury13
    Программист на «си с крестами» и не только
    Здесь нужны две операции: «присвоить» (возможно, автоматически сгенерированная) и конструктор копирования.
    class MP3_Format {
       MP3_Format(const char* c_string);
       MP3_Format& operator=(const MP3_Format&);   // велика вероятность, что компилятор её сгенерирует автоматически
    };


    Если пишете на C++11 — вместо операции «присвоить» нужна операция «присвоить из временного объекта». Опять-таки, годится автоматически сгенерированная.

    Семантика языка C++ в этой строчке такова. Мы создаём временный объект MP3_Format("Summertime"), а затем переносим его в наш mp3a. Скорее всего, оптимизатор уберёт этот перенос и инициализирует mp3a на месте, но такова уж семантика…
    Ответ написан
    1 комментарий
  • Почему появляется ошибка компилятора '?

    @Mercury13
    Программист на «си с крестами» и не только
    Ошибся тут не компилятор, а линкер. Этот вопрос идёт со времён Си и происходит из технологии компиляции: несколько единиц компиляции компилируются независимо, а потом собираются воедино компоновщиком (линкером).

    Статический член класса — это глобальная переменная, и если объявлять её в каждой единице компиляции, то какую потом линкеру брать? Вот мы и пришли к выводу: static int counter; в хедере — это тот же extern. Другими словами, мы не объявляем переменную, а говорим: не беспокойся, компилятор, в какой-то единице компиляции она будет.

    И решается этот вопрос так же, как для любого extern’а: в одной единице компиляции (CPP) объявляем
    int Skript::counter;
    Можно также статически инициализировать этот счётчик:
    int Skript::counter = 0;

    Ах да. А технология эта с единицами компиляции откуда? Из ассемблера. Если программа у нас почти на всю память в 16 килобайт, то ассемблерный текст намного, намного превышает память ПК. Вот и приходится ассемблировать по частям, а потом собирать куски полуготового машинного кода в полную программу. Наконец, чтобы язык заработал, КиР пришлось написать только компилятор Си, а линкер — имеющийся.
    Ответ написан
    Комментировать
  • Как указать путь создаваемого файла в c++?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Нам придётся работать с юникодными (wchar_t* / wstring) именами файлов. Использовать функции wfopen или CreateFile.
    2. Чтобы заполучить путь к рабочему столу, надо исполнить вот что.
    std::wstring desktopPath;
    wchar_t tmp[MAX_PATH + 1];
    if (SHGetSpecialFolderPathW(HWND_DESKTOP, tmp, CSIDL_DESKTOPDIRECTORY, FALSE))
        desktopPath = tmp;

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

    @Mercury13
    Программист на «си с крестами» и не только
    Вводить по символу, проверяя функцией isdigit, цифра это или нет.
    Ответ написан
    Комментировать
  • Как создать обьект с std::initializer_list?

    @Mercury13
    Программист на «си с крестами» и не только
    Это нельзя, initializer_list — это тонкая прослойка, позволяющая инициализировать вектор из линейного массива. Ни на что большее она не нужна.

    У вас есть какая-то странность — SearchVektor<unsigned int> мы инициализируем из вектора vector<int>. Это вообще ни в какие ворота не пишется. Для данного случая вам нужно…

    1. vector<int> liste превратить в vector<unsigned int>.
    2. Написать конструктор, работающий из временного вектора: SearchVektor(std::vector<T> && t ). Такой конструктор будет очень быстрым, ибо дерёт всю информацию из t, оставляя в нём пустоту.
    3. Объявить liste временным —
    return  SearchVektor< unsigned  int> (std::move(liste));
    Ответ написан
    4 комментария
  • Переопределить operator() для получения доступа к члену класса?

    @Mercury13
    Программист на «си с крестами» и не только
    class Window{
    public:
      Graphics& operator * () const { return *g; }
      Graphics* operator -> () const { return g; }
    private:
      Graphics *g;
    }
    
    ...
    
    window->Clear(Color::White);
    Ответ написан
    2 комментария
  • Как написать функцию которая может не вернуть значение?

    @Mercury13
    Программист на «си с крестами» и не только
    Object* find (const Object& o)
    {
        /* Поиск, если нашли */
       return link;
       /* Если не нашли? */
       return NULL; // Теперь можно
    }
    Ответ написан
    5 комментариев
  • Как использовать "гибкость" двумерного массива, перед одномерным?

    @Mercury13
    Программист на «си с крестами» и не только
    Итак, перед нами задача: сделать динамический массив «умных указателей единоличного владения». Умный указатель единоличного владения (std::unique_ptr из C++11) — это указатель, который самолично владеет выделенной памятью; при исчезновении указателя исчезает и память.

    Раз мы только учимся, мы не будем влезать в самописные шаблоны C++, готовые шаблоны STL (кроме algorithm и string) и новый, но уже реализованный во всех компиляторах стандарт C++11. Это довольно серьёзное ограничение; если его снять, можно серьёзно упростить себе жизнь. А для этого мы отделим структуру данных от жизненных объектов и реализуем объект StudentList. Пишу с листа, возможны ошибки.

    Да, и без C++11 умный указатель единоличного владения реализовать довольно тяжело — поэтому структуру данных будем делать «монолитно», без разделения на умный указатель и динамический массив.

    #include <algorithm>
    
    class StudentList
    {
    public:
      StudentList();
      ~StudentList();
      Student& add();   // добавить пустого студента и выдать ссылку на новенького
      size_t size() const { return fSize; }
      Student& operator[](size_t i) { return *fData[i]; }   // можно также наладить проверку диапазона — сделай это сам…
      const Student& operator[](size_t i) const { return *fData[i]; }
      void clear();
    private:
      typedef Student* PStudent;
      PStudent* fData;
      size_t fSize, fCapacity;   // реальное кол-во студентов и на сколько студентов у нас заведено памяти.
                            // Указатели [fSize..fCapacity) резервные, их значение не определено и высвобождать
                            // их не надо.
      enum { BLOCK_SIZE = 16; };
      StudentList(const StudentList&) {}   // копирование запрещаем, хочешь — реализуй сам и вынеси в public
      StudentList& operator=(const StudentList&) { return *this; }  // аналогично
    };
    
    StudentList::StudentList(); : fData(NULL), fSize(0), fCapacity(0) {}
    
    Student& StudentList::add()
    {
      // Убедиться, что массива хватает; если нет — расширить
      if (fSize >= fCapacity) {
        size_t newCapacity = fCapacity + BLOCK_SIZE;
        PStudent* newData = new PStudent[newCapacity];
        std::copy(fData, fData + fSize, newData);
        delete[] fData;
        fData = newData;
        fCapacity = newCapacity;
      }
      // Завести нового студента
      Student* r = new Student;
      fData[fSize++] = r;
      return *r;
    }
    
    void StudentList::clear()
    {
       for (size_t i = 0; i < fSize; ++i)
         delete fData[i];
       delete[] fData;
       fData = NULL;
       fSize = 0;
       fCapacity = 0;
    }
    
    StudentList::~StudentList()
    {
       clear();
    }


    Удаление и прочее сам наладишь?

    А группа будет пользоваться нашим списком.

    #include <string>
    
    class AcademyGroup
    {
    public:
       std::string name;
       StudentList students;  // при желании можно заинкапсулировать и его.
    };


    Это перед нами, правда, не двухмерный массив, как я уже сказал. Массив, хоть и Student**, но одномерный; каждый элемент массива — умный указатель единоличного владения. Если бы мы писали на STL C++11, это был бы std::vector<std::unique_ptr<Student>>.

    Кроме УУЕВ, существует также умный указатель совместного владения std::shared_ptr.

    Можно сделать второй вариант — массив из одномерных массивов. Если он заполнился — заводим ещё один массивчик. Пишется немного сложнее, особенно если не пользоваться STL. На STL — std::deque<Student>.
    Ответ написан
    6 комментариев
  • Как поймать в GDB исключение, вызванное из basic_string(const charT*)?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    Нашёл.
    Подключить статический libc++ (-static-libstdc++), тогда всё будет.
    Ответ написан
    Комментировать
  • Почему this NULL в istream_iterator?

    @Mercury13
    Программист на «си с крестами» и не только
    Операторы перегружены правильно.

    А вот и нет. Ваша ошибка
    std::istream& operator>>(std::istream& s, Pnt& p)   // убрать const!

    Не обязательно, но можно.
    explicit Pnt(int s) {…} // добавить explicit!
    Что получается? Мы пытаемся считать в неизменную p.a. Прямое s >> p.a; не подходит — поле-то неизменное. И тут, как ни странно, находится цепочка: создать временный Pnt и снова вызвать саму себя!

    Добавить explicit: запрещаем вызывать этот конструктор неявно.
    Убрать const: расфиксировать p.a.

    Также у вас ошибка в перегрузке операции «присвоить».
    Pnt& operator=(const int a) {
           this->a = a;
           return *this; // ну забыл!
         }


    Насчёт explicit. Конструкторы с одним параметром вообще стоит делать explicit всегда, когда объект семантически отличен от параметра. Например, VeryLongInt(10) или Complex(10) — это та же десятка, и explicit можно опустить. А Vector(10) — динамический массив из 10 элементов ≠ десятке, тут explicit нужен.
    Ответ написан
    1 комментарий
  • Динамическая структура данных. Что это?

    @Mercury13
    Программист на «си с крестами» и не только
    Инструментом для этого, опять-таки, послужит операция new.

    Это и есть критерий ДСД. Всё, где используется динамическая память,— динамическая структура данных.

    Так же яндексом выводится очень много задач/примеров, где дсд применяется к Стеку. В итоге получаем структуры и стек.

    Почему только?
    По организации: одиночный объект, массив, разные виды деревьев, связанный список и комбинации всего этого…
    По семантике: одиночный объект, индексный массив, ассоциативный массив, список, стек, очередь, pimpl (указатель на реализацию), динамическая строка…
    Например, ассоциативный массив (семантика) можно реализовать как дерево поиска (двоичное дерево), а можно как хэш-таблицу (расширяемый динамический массив + куча связанных списков).
    Очередь (семантика) может быть кольцевым списком (динамический массив, возможно, расширяемый), может — расширяемым массивом из динамических массивов (так работает std::deque из C++), может — связанным списком.
    Стандартный подход к строке — динамический массив. Но что делать, если мы, например, клеим и клеим к строке что-то в конец? Или пишем текстовый редактор, где можно вписывать текст в середину? Тоже хитрые структуры.
    Задача Яндекса — проверить испытуемого за несколько минут. Сложную задачу вряд ли за это время разгрызёшь. Потому и любят простые структуры данных.
    Ответ написан
    Комментировать
  • Почему в потоке cout элементы выводятся в другом порядке?

    @Mercury13
    Программист на «си с крестами» и не только
    Дело в том, что между точками следования (это точка с запятой, вызов функции и экономные операции &&, || и ?:) компилятор имеет право переставлять операции как хочет. Вот он и решил вычислять c++ с конца.

    В C++11 нет точек следования, есть «вычисляется прежде», но языка это кардинально не меняет.

    В C++17 это обещают поправить.
    19) In a shift operator expression E1<<E2 and E1>>E2, every value computation and side-effect of E1 is sequenced before every value computation and side effect of E2

    en.cppreference.com/w/cpp/language/eval_order

    ЗЫ. MinGW даже предупреждение выводит, что результат такой строчки явно не определён. И на классе Co тоже (см. флейм с Толстым Лорри).
    Ответ написан
  • Как объединить три одномерных массива в один?

    @Mercury13
    Программист на «си с крестами» и не только
    Я бы свалил их один за другим в один массив или вектор
    www.cplusplus.com/reference/algorithm/copy

    а затем дважды вызвал бы функцию
    www.cplusplus.com/reference/algorithm/inplace_merge

    Если массивы длины 10, 15 и 20, то…
    • Первый раз — std::inplace_merge(v.begin(), v.begin() + 10, v.begin() + 10 + 15);
    • Второй раз — std::inplace_merge(v.begin(), v.begin() + 10 + 15, v.begin() + 10 + 15 + 20);
    Ответ написан
    Комментировать
  • Не проходит компиляцию с любым текстом в TrayIcon->Hint();?

    @Mercury13
    Программист на «си с крестами» и не только
    Здесь используется расширение C++, т.н. свойства. Внешне они выглядят как поля, но при чтении-записи вызывают функции.
    Form1->TrayIcon->Hint = L"Bad";

    И ещё — название Form1 не рекомендуется, назови её как-нибудь fmMain.
    Ответ написан
    Комментировать
  • Как сэмулировать нажатие клавишы Пробел на Arduino?

    @Mercury13
    Программист на «си с крестами» и не только
    Странно: если ардуина — USB-клавиатура, то она передаёт именно нажатие физической клавиши «пробел».
    Впрочем, у вас тут стоит пробел в кавычках " ". Скорее всего, именно в этом ошибка: при плохой проверке типов, присущей Си, в функцию (которая требует char) будет передан адрес строки «пробел».

    Надо Keyboard.press(' '); или Keyboard.write(' ');. В одиночных кавычках. То есть целое число.

    Кроме того, Keyboard.press только нажимает на кнопку, и разбирай, как эту кнопку «отпустить». По крайней мере, моя «лисичка» требует именно ОТПУСКАНИЯ пробела. Как вариант — тот же Keyboard.write, который жмёт и отпускает.
    Ответ написан
    1 комментарий