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

    @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, без точных реализаций) ваших классов, и погоняем, что там неверного.
    Ответ написан
    Комментировать
  • В чем разница int mas[], int mas[0], int mas[100]?

    @Mercury13
    Программист на «си с крестами» и не только
    int mas[] используется в трёх местах.

    1. Когда размер определяется инициализатором: int mas[] = { 1, 2, 3, 4, 5 };
    2. Чтобы задать параметр вида «массив неопределённой длины»: double vecLen(double a[], int size) {}
    Си не может жёстко задавать размер массива в параметре, чтобы больший или меньший не подходил; даже если напишешь напишешь double vecLen(double a[3]) {}, всё равно другой массив подойдёт. Си++ задаёт так: double vecLen(double (&a)[3]) {})
    3. Подсказал jcmvbkbc, реально мало на что нужно: extern int a[].

    int mas[0] создаёт массив нулевой длины, надобности в котором, понятное дело, никакой. Зато этот код используется, чтобы накладывать структуру данных, которая заканчивается массивом неизвестной длины, на какой-то буфер в памяти — как указатель: тут массив (C++ не проверяет выход за границу).

    struct Packet {
      unsigned short length;
      unsigned char data[0];
    }
    
    void processPacket(void* data, unsigned length)
    {
       // Простите уж, что перешёл на C++
       const Packet& packet = *reinterpret_cast<Packet*>(data);
       if (length != sizeof(Packet) + packet.length)
         throw std::logic_error("Packet size mismatch");
       for (unsigned i = 0; i < packet.length; ++i) {}
    }
    Ответ написан
    8 комментариев
  • Как понять заголовочные файлы?

    @Mercury13
    Программист на «си с крестами» и не только
    #include — как ни странно.

    Вы так и не поняли такой вещи, как «единица компиляции». Дело в том, что в Си c-файлы компилируются независимо друг от друга (в единую программу всё собирает линкер). А чтобы сказать «оно есть, только в другой единице компиляции», используют прототипы и extern’ы.

    А если вы хотите просто внести код в ту же единицу компиляции, просто пишите его в хедере, да и всё. Только в большинстве компиляторов это исключает предкомпилированные хедеры — а ТАКИЕ хедеры вам предкомпилировать, скорее всего, и не нужно.

    <брюзга mode on>
    Не создают кода (а значит, в традиционной системе с кучей единиц компиляции находится именно в хедерах)
    • extern и прототипы
    • inline
    • не до конца специфицированные шаблоны
    • static-поля в классе (но потом это static-поле придётся повторить в какой-нибудь одной единице компиляции)
    • может, ещё что-то, только я забыл…
    <брюзга mode off>
    Ответ написан
    1 комментарий
  • Как дополнить топологическую сортировку?

    @Mercury13
    Программист на «си с крестами» и не только
    С двузначными метками посещения (bool used[]) — никак.

    Надо перейти на трёхзначные метки (UNVISITED, PARTLY_VISITED, VISITED).

    void dfs (int v) {
      state[v] = PARTLY_VISITED;
        .....
      state[v] = VISITED;
      ans.push_back (v);
    }


    Неплохой пример есть в вашем предыдущем вопросе.
    Ответ написан
    Комментировать
  • Как применить топологическую сортировку?

    @Mercury13
    Программист на «си с крестами» и не только
    Нужно переписать под матрицу смежности вот этот участок кода.
    for(int i = 0;i < Edges[v].size();i ++){
              if(dfs(Edges[v].get(i)))return true;
          }

    Что-то типа этого.
    цикл (i : все вершины)
       если матрица_смежности[v, i]
             если dfs(i)
                 return true;

    Ещё мне не нравятся константы 0, 1 и 2. Объявить бы их как
    UNVISITED = 0;
    PARTLY_VISITED = 1;
    VISITED = 2;

    Или WHITE, GRAY и BLACK, если хотите — в учебниках по алгоритмам их красят в три цвета.
    Ответ написан
    Комментировать
  • Как правильно написать MutexLocker?

    @Mercury13
    Программист на «си с крестами» и не только
    pthread_mutex_locker locker(&mutex);

    Назови его как-нибудь, и всё будет окей.
    Ответ написан
    Комментировать
  • Зачем нужен ООП?

    @Mercury13
    Программист на «си с крестами» и не только
    Я хотел спросить: вы что, студент? Потом посмотрел: нет, вебист. Вебистам действительно ООП нужно крайне редко; если не связывался с хитрой поддержкой сложных протоколов или со сложными моделями данных — в памяти, не в БД — можно писать без ООП и быть успешным вебистом.

    Я и сам долгое время не понимал, на что нужны эти объекты. Главный вопрос: ДЛЯ ЧЕГО?

    Для того, чтобы передать взаимодействие кучи вещей. Где нет взаимодействия, там нет ООП, и можно заниматься, например, научными расчётами, на которые мало кто из нас способен, высший пилотаж, и вообще про ООП ничего не знать. Помню, как Вассерман — тот самый, некогда программист техпроцессов — долго-долго вспоминал, что такое ООП.

    Предлагаю начать с простого.

    1. Объектный синтаксис. Было…
    iDog : integer;
    iDog := SpawnMonster(world, mtDog, x, y);
    ChargeAtPlayer(iDog);


    Стало…
    dog : TMonster;
    dog := world.SpawnMonster(mtDog, x, y);
    dog.ChargeAtPlayer;


    Уже стало красивее. Да и номер собаки можно случайно передать, например, вместо номера оружия; с типом TMonster такой фокус не пройдёт.

    2. Инкапсуляция.
    Это мы уже думаем над тем, чтобы вызовы функций не могли привести объект в «ненадлежащее» состояние, а всё, что может объект подпортить,— засунуто в private.

    3. Абстракция и наследование. Это уже «сложный пилотаж». Не высший — это неотъемлемая часть навыков хорошего программиста, да и для 80% задач это не нужно, зато в остальных 20-и очень улучшает жизнь. Самый простой пример. В какой-нибудь 2D-игре есть некий TGameObject, у которого виртуальные функции Live и Render. Первая прокручивает такт «жизни» объекта, вторая рисует его на экране. TGameObject можно разбить на TPlayer, TProjectile, TEnemy и TBonus, и т.д.

    Ах да. Для ООП не нужен объектно-ориентированный язык и объектный синтаксис, нужно объектно-ориентированное мышление. Например, Doom был написан на Си, но в очень-очень объектном стиле.
    Ответ написан
    Комментировать
  • Какой аналог php функции is_string() в ЯП С++?

    @Mercury13
    Программист на «си с крестами» и не только
    C++ — язык со статической типизацией, и если x объявлено как string, то он всегда string. А если как int, то он никогда не string. Поэтому функция is_string в C++ просто не имеет смысла.

    Правда, существуют островки динамической типизации наподобие VARIANT из OLE — это уже смотрите по месту (msdn.microsoft.com/en-us/library/cc237865.aspx) и, скорее всего, это не ваш вопрос.

    Да, вы хотели проверять корректность ввода. Например, для проверки, будет ли строка числом, можно использовать www.cplusplus.com/reference/cstdlib/strtod (не забудьте потом проверить endptr!)
    Ответ написан
    Комментировать
  • Вопрос специалистам по GCC и темплейтам C++

    @Mercury13
    Программист на «си с крестами» и не только
    Всё, что я знаю: GCC — самый «въедливый» компилятор. После Intel и Embarcadero (про M$ не знаю) на «зашаблоненном» коде GCC найдёт ещё два десятка ошибок.
    Ответ написан
    Комментировать
  • Возможно ли создать "универсальный" драйвер для исполнения произвольного кода в Ring0?

    @Mercury13
    Программист на «си с крестами» и не только
    Это к Sony или StarForce. И те, и другие делали заSHITу и прокалывались на подобном.
    Ответ написан
  • Можно ли создавать в рантайме объект в C++?

    @Mercury13
    Программист на «си с крестами» и не только
    Разрешите вставить и свои 5e−2 Р

    Известно ли что-то о том, какой тип будет иметь наш auto? Если известно, то может, сработает просто

    dynamic_cast<Type>(object).field1.field2 = NULL;
    Ответ написан
    Комментировать
  • Разобраться со слабыми сторонами C++?

    @Mercury13
    Программист на «си с крестами» и не только
    Что я могу сказать про проблемы C++?
    1. Слишком слабая типизация. Например, int x = 0.0;
    2. Система хедерных файлов крайне медленна, «предкомпилированные хедеры» и extern template — полумеры.
    3. Запутано подключение чужого откомпилированного кода (DLL, к примеру). Мало написать хедер, надо ещё откомпилировать lib — в общем, интересного мало.
    4. Библиотека STL крайне жирна. Хотя и libc тоже «хороша» — минимальная программа на Паскале занимала несколько килобайт, в зависимости от компилятора, на Си — приближается к сотне килобайт. Я не говорю про Linux/MSVC, где libc динамически подключаемая.
    5. Строковый литерал на C++ — это та же нуль-терминированная строка. Когда эту строку приходится оборачивать в какой-нибудь std::string, уже при выполнении вычисляется её длина. Зачем? Почему бы не вкомпилировать её в exe'шник?
    6. Нет ключевых слов override/reintroduce. При изменении сигнатуры виртуального метода приходится вспоминать, где он переопределялся.
    7. Нет виртуальных конструкторов. «Фабрика» — полумера.
    8. Коряво реализовано право доступа «читай кто угодно, пишу только я».
    9. Явное определение методов как inline или не-inline в сочетании с шаблонами приводит к странным эффектам. Когда расшаблонивание приводит к сложному коду, inline вреден (сжирает кэш процессора), когда к простенькой операции с указателем — наоборот, нужен. В общем, это давно уже должно стать парафией оптимизатора.
    10. В разного рода callback'ах замыкание приходится реализовывать собственными силами. Что-то типа: typedef void (*ProcDoSomething)(int aParam, void* aClosure). То же самое в Delphi: type ProcDoSomething = procedure(int aParam) of object;
    11. Если вдруг случайно два разных модуля реализуют одно и то же, но один препроцессором, а второй — синтаксисом C++, будет ОЧЕНЬ много геморроя с поиском ошибки.
    12. В обычном цикле for счётчик упоминается трижды. В общем, место очень ошибкоопасное. Для самых простых циклов у меня вообще есть макрос FOR_S (i, 0, n); суффикс S означает size_t.
    13. Когда из-за рефакторинга «внутренней кухни» объекта меняется способ хранения ссылки, меняется и код, который этой ссылкой пользуется. Например: object.buddy.field, object->buddy.field, object.buddy().field — в зависимости от того, buddy реализовано как Buddy& buddy, Buddy* buddy или Buddy buddy().

    Пока, засиделся. Мне бежать.
    Ответ написан
    4 комментария