Ответы пользователя по тегу C++
  • Для чего же нужны указатели?

    @Mercury13
    Программист на «си с крестами» и не только
    Хорошо, попробую. Вот у нас есть файл на каком-нибудь NAS. Мы хотим его переслать товарищу. Мы можем…
    • приложить файл к сообщению, тогда у нас будут два независимых файла. Просто и надёжно, недостатка два. 1) Файл копируется, это трудоёмко. 2) Копия живёт своей жизнью, и изменения в копии не затронут оригинал. Это передача по копии.
    • дать имя файла. Один изменяет файл — второй прочитает изменение, и никакого копирования. Недостаток — надо договариваться, кто владеет файлом (т.е. уничтожит его, когда тот не будет нужен). Имя файла — это и есть указатель.
    Метафора неполная, ведь файловая система может сказать, есть ли файл, а в памяти указатель на отсутствующий «файл» — это Access Violation. Да и «учёток» в памяти нет. Тем не менее…
    Ответ написан
    Комментировать
  • Как разобрать матрицу трансформации на состовляющие?

    @Mercury13
    Программист на «си с крестами» и не только
    Как я понял, твоя матрица 3×3 — это однородные координаты в 2D? Я бы поступил так.
    1. Убедиться, что элементы 3-1 и 3-2 нули (иначе — это не аффинное преобразование).
    2. Элемент 3-3 превратить в единицу, соответственно увеличив остальные (на что — читай, что такое однородные координаты).
    3. Элементы 1-3 и 2-3 — перенос. Отрежем их, получается матрица 2×2.
    4. То, что осталось, должно быть вида (c, s), (-s, c). Если с какой-то погрешностью это не так и 2-норма строк не единица (тоже с какой-то погрешностью) — это не поворот (т.е. может быть масштабирование или наклон). Остаётся взять atan2(c, s) — получается угол.
    Ответ написан
    Комментировать
  • Задача по методу Эйлера, за что отвечает функция F в данном коде?

    @Mercury13
    Программист на «си с крестами» и не только
    Качество кода — на уровне студенческой лабораторной. Причём очень хреновой.

    Программа решает четыре системы.
    y = f0(t)
    y' = f1(t, y)
    y'' = f2(t, y, y')
    y''' = f3(t, y, y'')

    1. Если уж говорить о методе Эйлера — в программе ошибка.
    y[1] = y[1] + delt * y[2];
    Тут нужно старое значение y[2], а не новое.

    2. Функцию eiler стоило бы обозвать eulerStep, заодно убрав лишние параметры.

    3. Автор не знает такой концепции, как процедурный тип aka callback, поэтому работа с правой частью у него вышла вот через такую задницу.

    4. В промышленном коде метод решения ОДУ я бы вынес в отдельную процедуру, с callback’ами на правую часть и на потребителя результата. Причём внешнее (доступное пользователю библиотеки) именование должно быть такое, чтобы её пользователь мог ею воспользоваться, даже если он не читал статью, по которой библиотеку писали. Так что смотрите, какие имена являются стандартизированными, понятными по любому учебнику, а какие стоит прояснить. Внутреннее именование — это уж думайте сами, научный код обычно сложен, и его постоянно приходится сличать со статьёй. Поэтому имена как в статье — для научного кода не WTF.

    5. Не слишком удачно разделена ответственность между функцией правой части и функцией шага по Эйлеру. Это привело к ограничению — программа может решать только ОДУ y{n} = f(t, y, y', ..., y{n−1}).
    Ответ написан
    8 комментариев
  • Надо решить проблему с include?

    @Mercury13
    Программист на «си с крестами» и не только
    Учи понятие «единица компиляции».

    Два варианта.
    a) Несколько единиц компиляции (основной вариант, когда идёт активная разработка проекта). Тогда в хедере каждую переменную отмечаешь модификатором extern — «переменная есть, но где-то в другом месте». В одной из единиц компиляции объявляешь переменные, уже без extern.

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

    ///// unit.h /////
    #ifndef UNIT_H
    #define UNIT_H
    
    extern int myVar;
    
    #endif
    
    ///// unit.cpp /////
    #include "unit.h"
    
    int myVar;
    
    ///// main.cpp /////
    #include "unit.h"
    
    int main()
    {
       myVar = 1;
    }


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

    ///// unit.h /////
    #ifndef UNIT_H
    #define UNIT_H
    
    int myVar;
    
    #endif
    
    ///// main.cpp /////
    #include "unit.h"
    
    int main()
    {
       myVar = 1;
    }


    ЗЫ. Когда я писал ответ, кода ещё не было. А тут он взял и пришёл. Для переменных (SYMBOL_WIDTH, LINE_HEIGHT) решение то самое. А для констант вариантов несколько.

    Путь сишный.
    #define LEFTPANEL 200
    ВОЗМОЖНО для литеральных констант, и особенно классно работает с текстовыми строками (например, "[" TEXT "]").
    НЕДОСТАТКИ: это препроцессор, и при пересечении имён будет нехорошо.

    Путь перечислимый.
    enum {
      LEFTPANEL = 200
    };
    ВОЗМОЖНО для констант, влезающих в int.
    НЕДОСТАТКИ: возможны приколы с кроссплатформенностью, если на отдельных платформах константа вылезет за грань int.

    Путь переменный.
    extern const int LEFTPANEL;
    ....
    const int LEFTPANEL = 200;
    ВОЗМОЖНО для любых констант.
    НЕДОСТАТКИ: нет вычисления при компиляции. Только линкер знает, что это 200.

    Путь C++11.
    constexpr int LEFTPANEL = 200;
    ВОЗМОЖНО для литеральных констант.
    НЕДОСТАТКИ: только C++11.

    Путь «на рывок»
    static const int LEFTPANEL = 200;
    ВОЗМОЖНО для всех литеральных типов, но при этом может создавать дублирующиеся переменные: так, в Borland будет дублироваться всё сверх int — и, соответственно, не допускать предкомпилированных заголовков, ибо создаётся код.
    НЕДОСТАТКИ: семантика зависит от компилятора и типа, ради чего и сделали constexpr.
    Ответ написан
    2 комментария
  • Как избавиться от зависания формы при выполнении работы в потоке через Synchronize?

    @Mercury13
    Программист на «си с крестами» и не только
    Как ни странно, не использовать Synchronize на долгих операциях! Сабж выполняет операцию в главном потоке и, значит, главный поток всё это время не отвечает.

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

    Можно использовать PostMessage, правда, никто не знает, когда форма подберёт сообщение и нужно чётко следить за тем, кто чем владеет.
    Ответ написан
    Комментировать
  • Для чего нужен данные предикаты?

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

    Некоторые поисковые функции STL требуют для работы функцию. Например, www.cplusplus.com/reference/algorithm/find_if или www.cplusplus.com/reference/algorithm/sort (второй вариант).

    А что делать, если какие-то параметры поиска или сортировки задаются программно? Можно, конечно, эти параметры сделать глобальными переменными — но, как известно, «избегайте незнакомых женщин и глобальных переменных». В таком случае используют т.н. функтор — некий объект с операцией (). Другими словами, его «вызывают», как функцию. Все изменяемые параметры будут храниться в полях этого объекта.
    Ответ написан
    Комментировать
  • Почему ругается на дружественную функцию?

    @Mercury13
    Программист на «си с крестами» и не только
    Причина, вероятно, в том, что ты допустил код в хедере, и sort_pred оказался реализован дважды в разных единицах компиляции.
    Либо сделай sort_pred inline (inline кода не производит, производит его тот, кто этот inline вызовет), либо вынеси его в cpp-файл.

    Ах, да. Почему всё правильно с этой функцией…

    bool operator()(two *t)
    {
        return t->getA()==a;
    }


    А потому что тело внутри класса автоматически inline и тоже кода не производит.
    Ответ написан
    Комментировать
  • Как правильно обрабатывать изображения в стороннем потоке?

    @Mercury13
    Программист на «си с крестами» и не только
    Что я пока вижу.
    1. В любой высокоуровневой библиотеке CreateThread использовать ЗАПРЕЩЕНО, вместо этого используй beginthreadex (или что-то ещё, предоставленное библиотекой). Или, раз уж ты по-чёрному используешь VCL, особо не убудет, если будешь использовать TThread.
    2. Lock и Unlock — это обыкновенный мьютекс. Все конкурирующие потоки, кому захочется рисовать на холсте, ждут и курят, ничего не делая.
    3. Самое-то главное ты упустил. Если ждёшь помощи, не нужно так секретничать, что не поймёшь, в чём дело. Где — хотя бы примерно — тело потока, и что делается в главном потоке?
    4. А как надо работать? Есть некая структура данных, в которой мы возвращаем из потока обработанные картинки, и volatile bool isWorking, который отвечает за то, работает поток или нет. Когда этот флажок false, главный поток имеет право работать с нашей структурой. Он переключается в true — в структуре может быть любой мусор, обращаться к ней запрещено! Если ещё и надо предупредить, когда поток закончился и можно забирать информацию — TThread.Synchronize или PostMessage. То и другое работает только в GUI-программах, если консольная — то через события (CreateEvent).
    5. А почему «нельзя этого делать»? Если партия приказала, значит, надо. Просто многопоточность — это тяжёлое дело, ошибки на каждом шагу, но «принципиально нельзя» — нет такого.
    Ответ написан
    4 комментария
  • Почему получается странная кодировка в файле?

    @Mercury13
    Программист на «си с крестами» и не только
    msc.insert(dat);
    Перед этой строчкой dat правильный, проверь?

    Если правильный — возможно, виновата ваша коллекция.
    Ответ написан
    1 комментарий
  • Странная кодировка в файле,с++?

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

    char* — это просто указатель на чей-то другой отрезок памяти. И программист должен чётко осознавать его время жизни. Скорее всего, строка исчезла раньше, чем указатель. Используй автоматические строки наподобие std::string.

    Кроме того, std::map будет, если я не ошибаюсь, сортировать по адресам, а не по строкам. Чтобы всё работало, надо писать свою операцию <, читай документацию, как.
    Ответ написан
    6 комментариев
  • Как инициализировать строку PCHAR в с++?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Что принимает система: PCHAR или PCHAR*?
    2. Если версии Delphi достаточно новые, PCHAR — это, скорее всего, wchar_t* и надо просто передавать «широкую» строчку L"usb3000".
    3. Постарайся, чтобы reinterpret_cast не было; это признак того, что ты делаешь что-то неверно. Единственное, где он в подобных API допустим — это в callback’ах для передачи замыкания (информации о том, какая подпрограмма и при каких обстоятельствах запустила функцию, вызвавшую callback). P.S. Надумал и второе назначение reinterpret_cast — функция, которая может принимать данные разных типов (вроде SendMessage из Win32).
    Ответ написан
    Комментировать
  • Как построить N-дерево и найти самую длинную ветвь без ветвлений?

    @Mercury13
    Программист на «си с крестами» и не только
    Что-то типа

    максРазмер : целое
    началоМаксВетви: узел
    
    проц максВетвь(узел : Узел, началоВетви: Узел, текРазмер: целое)
      в зависимости от степени узла «узел»
        если 0:
          если текРазмер > максРазмер
            максРазмер = текРазмер
            началоМаксВетви = началоВетви
        если 1: максВетвь(единственныйCын, началоВетви, текРазмер + 1)
        если 2+:
          для всех сыновей
            максВетвь(сын, сын, 1)
    
    максВетвь(корень, корень, 1)


    Считает макс. ветвь, кончающуюся листом; если может кончаться и развилкой — оставляю как домашнее задание.
    Ответ написан
    Комментировать
  • Как устранить коллизии?

    @Mercury13
    Программист на «си с крестами» и не только
    Первое. Заводи массив, в простейшем случае (таблица ограниченного размера) — как минимум вдвое больше, чем желаемая ёмкость. В каждом гнезде или пара (ключ, значение), или «ничего». То ли какое-то спец.значение ключа отведи на «ничего», то ли храни указатели на элементы (не забудь наладить управление памятью), то ли сделай два параллельных массива, один Entry[], второй bool[] (можно добавить bool в Entry, так лучше с прогерской точки зрения, но перерасход памяти).

    Второе. Вычисляем хэш, берём остаток от деления на n, заглядываем в таблицу. Если там что-то есть, но у этого «чего-то» другой ключ, перескакиваем на 1 вперёд. Если снова неправильный ключ — то ещё на два, если снова — то ещё на три… Надеюсь, поймёшь, как делать цикл…

    Третье. Зачем тебе map, если задача — сделать свою хэш-таблицу, а не пользоваться чужим самобалансирующимся деревом.

    Такая таблица может только расширяться, удаление если и можно наладить, то тяжело. Википедия рекомендует второе спец.значение (кроме «ничего») — «тут когда-то что-то было». При поиске «тут когда-то что-то было» считаются занятыми, а при добавлении — свободными. Дурость в общем — удаление не упрощает поиск.
    Ответ написан
    Комментировать
  • Какую библиотеку по работе с POP/IMAP можно выбрать?

    @Mercury13
    Программист на «си с крестами» и не только
    cURL?
    Кроссплатформенная, кстати.
    Ответ написан
  • Как правильно переопределить оператор присваивания с инициализацией методов классов-родителей?

    @Mercury13
    Программист на «си с крестами» и не только
    Попытайтесь выяснить, кто не терпит тупого поэлементного копирования, и изолировать их. Либо в один маленький объектик, либо в один маленький уровень наследования.

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

    class LevelObject
    {
    private:
       Level& level;  // Сцылка на уровень, по определению неизменная
       int x, y;           // Присваивать.
    }


    Разбиваем на две части (внимание! — конструктор копирования для простоты не прописан, вероятно, потребуется и он):
    #include <iostream>
    
    class Level
    {
    public:
        int value;
        Level(int aValue) : value(aValue) {}
    };
    
    class LevelChild
    {
    protected:
        Level& level;  // не присваивать
    public:
        LevelChild(Level& aLevel) : level(aLevel) {}
        LevelChild operator=(LevelChild&) { return *this; }   // ничего не делающая операция «присвоить»
    };
    
    class LevelObject: public LevelChild
    {
    protected:
        int x, y;
    public:
        LevelObject(Level& aLevel, int aX, int aY) : LevelChild(aLevel), x(aX), y(aY) {}
        void print();
    };
    
    void LevelObject::print()
    {
        std::cout << "(" << x << ", " << y << ") at level " << level.value << std::endl;
    }
    
    int main()
    {
        Level l1(1);  LevelObject o1(l1, 1, 2);
        Level l2(2);  LevelObject o2(l2, 3, 4);
        o1.print();
        o2.print();
        o2 = o1;
        o2.print();
    }


    Вариант 2. Сделать маленькие объекты-функции со своими операциями присваивания.

    class LevelObject
    {
    private:
       Level* level;  // На сей раз уже копируется
       Array3 pos;    // Какой-то инкапсулированный объект-массив
       std::wstring name;   // тоже инкапсулированный объект
    }


    И всё, когда для std::wstring и Array3 корректно прописаны операции присваивания, для LevelObject всё будет работать корректно без единой строчки.

    P. S. А точно ваш объект страдает от поэлементного копирования и нужна своя операция?
    Ответ написан
    Комментировать
  • Почему срабатывает конструктор копирования?

    @Mercury13
    Программист на «си с крестами» и не только
    Операция присваивания, по договорённости, должна возвращать Pol&.
    Ответ написан
    Комментировать
  • Как в CBuilder организовать объявление свойства?

    @Mercury13
    Программист на «си с крестами» и не только
    Вот, допустим, подсказка по XE2.
    Data.Win.ADODB.TADOQuery
    
    Type  | Visibility | Source             | Unit           | Parent 
    class | public     | Data.Win.ADODB.pas | Data.Win.ADODB | Data.Win.ADODB 
                       | Data.Win.ADODB.hpp |


    Вот и подключаешь <Data.Win.ADODB.hpp>. В более ранних версиях Builder придётся подключать просто <ADODB.hpp>.
    Ответ написан
    1 комментарий
  • NIL в красно-черном дереве?

    @Mercury13
    Программист на «си с крестами» и не только
    Исходник, конечно, дай — посмотрим, что там можно сделать. Как вариант, можно сделать так.

    Node nullNode = { NULL, NULL, NULL, BLACK };
    class NodeHandle {
    private:
      Node* data;
      Node* getData() const { return data ?data : &nullNode; }
    public:
      NodeHandle() : data(&nullNode) {}
      NodeHandle(Node* x) : data(x) {}
      NodeHandle& operator=(Node* x) { data = x; return *this; }
      bool operator == (const Node* x) const { return (data == x); }
      bool operator != (const Node* x) const { return (data != x); }
      bool operator == (const NodeHandle& x) const { return (data == x.data); }
      bool operator != (const NodeHandle& x) const { return (data != x.data); }
      Node* operator ->() const { return getData(); }
      Node& operator *() const { return *getData(); }
      operator Node*() const { return getData(); }
    }


    Но, думаю, всё же где-то вкралась ошибка.
    Ответ написан
    Комментировать
  • Почему происходит ошибка при обращении к полю статического класса?

    @Mercury13
    Программист на «си с крестами» и не только
    Дело в том, что static-член в теле класса — это только заголовок, говорящий, что такое есть, но не создающий ни кода, ни данных. Эквивалент extern. Где-то в CPP (т.е. в одной и только в одной единице компиляции — не в .h!) нужны…

    std::vector LogVoting::RegisteredDeputy;

    И так далее.

    Если этим полям нужен нестандартный конструктор — пожалуйста…
    std::vector LogVoting::RegisteredDeputy(42);
    Ответ написан
  • Что такое нарушения принципов ООП?

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

    Инверсия абстракции. Простые вещи, которые, вероятно, понадобятся потомкам, недоступны даже через protected.

    Нарушение принципа Лисков (ломаная абстракция). Для отца вы сделали некое предположение, которое неверно для сыновей. Классический пример — прямоугольник и квадрат — предполагается, что отец может произвольно масштабироваться, что неверно для сына.

    Класс вместо интерфейса. Если можно, родителя делайте классом без данных с двумя видами функций: public virtual = 0, и protected/public не-virtual (т.н. интерфейс с утилитами). Наследоваться от нескольких классов с данными очень некузяво (а во многих языках вообще невозможно).

    Всемогущий родитель. Слишком много функциональности придумали родительскому классу.

    В общем, покажите интерфейсы (protected/public, без точных реализаций) ваших классов, и погоняем, что там неверного.
    Ответ написан
    Комментировать