Задать вопрос
  • Объясните толком про интерфейсы в ООП (Delphi). Как их использовать?

    @Mercury13
    Программист на «си с крестами» и не только
    Интерфейсы в Delphi отвечают за две малосвязанных вещи.
    1. Множественное наследование. Об этом уже рассказали до меня, повторяться не буду.
    2. Подсчёт ссылок (для этого реализатор должен корректно поддерживать _AddRef и _Release, но это уже другой вопрос, и подходящая реализация есть в TInterfacedObject).
    Связано это с тем, что Delphi должен был поддерживать Microsoft COM, а там автоматическое управление через подсчёт ссылок.
    Так что интерфейсы часто приплетают только потому, что удобно работать с подсчётом ссылок.

    Вот, например, моя библиотека (обёртка cURL для Delphi) под названием curl4delphi: https://github.com/Mercury13/curl4delphi
    На что тут ICurl? А на то, что это объект с подсчётом ссылок, и для него не надо вызывать деструктор. Пропадает последняя ссылка — объект исчезает. Вот и всё.
    Из-за автодеструкторов, «киллер-фичи» Си++, я в Си++ так не поступал бы.
    Ответ написан
    Комментировать
  • PHP функциональный язык или объектно-ориентированный?

    @Mercury13
    Программист на «си с крестами» и не только
    PHP изначально задумывался скриптовым языком — языком процедурного программирования с динамической типизацией и возможностью вписать пару строк, не оформляя тело. Таким он и остаётся поныне, с вкраплениями ОО. Немного функциональщины, конечно, есть, но это не делает PHP — как, впрочем, Java или C++ — настоящим функциональным языком.
    Ответ написан
    Комментировать
  • Как узнать путь к Application Data текущего пользователя из службы?

    @Mercury13
    Программист на «си с крестами» и не только
    Служба может работать, даже если ни один пользователь не прилогинен к системе. Или если прилогинены двое (например, один оставил комп с работающим клиентом, подлогинился второй). Потому придётся переделать архитектуру службы — например, передавать настройки при установке связи клиента со службой.
    Ответ написан
    4 комментария
  • Как правильно унаследовать виджет QT?

    @Mercury13
    Программист на «си с крестами» и не только
    Наследоваться-то надо от QListWidget…
    Ответ написан
    2 комментария
  • C++ удаления дубликатов, почему не работает?

    @Mercury13
    Программист на «си с крестами» и не только
    void Wid::test() {
        vector<string> stp;
        stp.push_back(text->toPlainText().toStdString());
        // В stp одна штука
        sort(stp.begin(),stp.end());
        // Ну что ей будет, этой штуке?
        stp.erase(unique(stp.begin(),stp.end()),stp.end());
        // unique даст end; поведение vector при этом не определено.
        ofstream filesave(pp);
        copy(stp.begin(),stp.end(), ostream_iterator<string>(filesave,"\n"));
    }
    Ответ написан
    3 комментария
  • C++QT5: UTF-8, QString, QByteArray, *char и русские символы. Как изменить элемент в массиве *char?

    @Mercury13
    Программист на «си с крестами» и не только
    В кодировке UTF-8 один символ — это от одного до четырёх байт. Смиритесь, и если хотите менять русскую букву (2 байта) на английскую (1 байт), лучше работать в кодировке UTF-16.
    word1 = word;
    word1[0] = 'X';

    или
    word1 = QString::fromUtf8(ar.data());
    word1[0] = 'X';

    А если нет возможности — то разбирать UTF-8, разумеется.

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

    @Mercury13
    Программист на «си с крестами» и не только
    Неплохие результаты получились с одной из первых версий с простейшим патчем на повторы.
    #include <iostream>
    
    struct Sector
    {
        char name;
        int total, counter, remainder;
        bool allowNearby;
    };
    
    enum { N = 3 };
    Sector secs[] { { 'A', 3 }, { 'B', 6 }, { 'C', 30 } };
    
    int main()
    {
        int nTotal = 0;
        for (int i = 0; i < N; ++i) {
            Sector& sec = secs[i];
            nTotal += sec.total;
        }
        for (int i = 0; i < N; ++i) {
            Sector& sec = secs[i];
            sec.counter = sec.total;
            sec.remainder = sec.total;
            sec.allowNearby = (sec.total * 2 > nTotal);
        }
    
        Sector* repeatS = nullptr;
        for (int iSec = 0; iSec < nTotal; ++iSec) {
            Sector* bestS = nullptr;
            for (int i = 0; i < N; ++i) {
                Sector& sec = secs[i];
                if (sec.remainder != 0 && &sec != repeatS) {
                    if (!bestS) {   // первый подходящий?
                        bestS = &sec;
                    } else {    // лучше bestS?
                        if (sec.counter < bestS->counter
                                || (sec.counter == bestS->counter && sec.total > bestS->total))
                            bestS = &sec;
                    }
                }
            }
            if (!bestS)     // так и не нашли, что брать — берём repeatS
                bestS = repeatS;
    
            // пересчитаем счётчик и остаток
            bestS->counter += nTotal * 2;
            --bestS->remainder;
    
            for (int i = 0; i < N; ++i) {
                Sector& sec = secs[i];
                sec.counter -= sec.total * 2;
            }
            repeatS = bestS->allowNearby ? nullptr : bestS;
            std::cout << bestS->name;
        }
        std::cout << std::endl;
    }

    Пишу на «си с крестами». Раз уж вы работаете на PHP, вместо указателей придётся держать «лучший индекс» и «последний индекс».

    Принцип программы прост. Счётчики считают вниз с разной скоростью; чем чаще встречается сектор, тем быстрее. Приоритет взятия такой: не исчерпан → без повторов → у кого счётчик меньше → какой встречается чаще. Однако если какого-то сектора больше 50%, для него повторы вполне себе разрешены.
    Ответ написан
  • Как раздать интернет с модема на два wi-fi роутера?

    @Mercury13
    Программист на «си с крестами» и не только
    Что собой представляет кабельный модем в плане модели OSI?
    1. Маршрутизатор. Тогда подключайте к нему что угодно, хоть хабы, хоть маршрутизаторы. Главное — поставить NAT на самом модеме и отключить на маршрутизаторах (или даже поставить маршрутизаторы в режим точек доступа).
    2. Мост. Тогда подключаем маршрутизатор и налаживаем на нём NAT. К нему подключаем второй маршрутизатор, либо переведя его либо в режим точки доступа, либо просто отключив NAT.
    Ответ написан
    1 комментарий
  • Как комментировать код в Delphi так, чтобы при запуске приложения на windows код от андроид не исполнялся?

    @Mercury13
    Программист на «си с крестами» и не только
    {$IFDEF MSWINDOWS}
    // тут код, специфичный для Windows
    {$ENDIF}


    И, аналогично, {$IFDEF ANDROID}

    Часто используют {$IF defined(MSWINDOWS)} — можно написать {$ELSEIF defined(ANDROID)}.
    Ответ написан
    1 комментарий
  • Какой способ организации чтения и записи файлов в разных форматах оптимальнее?

    @Mercury13
    Программист на «си с крестами» и не только
    Портяночный способ хорош, когда на выходе — файл простой структуры. Например, стандартные файлы межпрограммного обмена наподобие HTML, PNG…
    Рекурсивный удобнее для внутреннего формата хранения, когда надо в подходящем виде сериализовать всё, что в программе есть, да ещё и наладить поддержку старых версий этой сериализации.
    Ответ написан
    Комментировать
  • Как создать компонент в потоке и оставить его в delphi?

    @Mercury13
    Программист на «си с крестами» и не только
    Можно, только синхронно. Используй TThread.Synchronize. Код пишется с листа, не уверен, что действующий.
    procedure TMyThread.SyncCreateMemo;
    begin
     Memo:=TMemo.Create(Form6);
      Memo.Parent:=Form6;
      Memo.Left:=50;
      Memo.Top:=50;
      Memo.Width:=250;
      Memo.Height:=100;
      Memo.Text:='Мама я родился!';
    end;
    
    procedure TMyThread.Execute;
    begin
      Synchronize(SyncCreateMemo);
    end;


    Второй способ, более сложный и опасный — PostMessage. Здесь никаких задержек поток испытывать не будет. Но будьте осторожны — форма создаст компонент когда угодно и нельзя его заполнять сразу же.
    procedure TForm6.WmCreateMemo; // message WM_CREATEMEMO = WM_USER + 1
    begin
     Memo:=TMemo.Create(Form6);
      Memo.Parent:=Self;
      Memo.Left:=50;
      Memo.Top:=50;
      Memo.Width:=250;
      Memo.Height:=100;
      Memo.Text:='Мама я родился!';
    end;
    
    procedure TMyThread.Execute;
    begin
      PostMessage(Form6.Handle, WM_CREATEMEMO, 0, 0);
    end;


    Заполнять наш редактор тоже можно путём PostMessage, но тут тоже будьте осторожны: сообщения-то выполнятся по порядку, но никто не знает, до окончания потока или нет. Бывает и такое, что два сообщения ушли в очередь, но ещё не обработались. В общем, будьте предельно осторожны с временем жизни объекта.
    Ответ написан
    Комментировать
  • Паскаль. Переменные и массивы внутри классов?

    @Mercury13
    Программист на «си с крестами» и не только
    Что творится? Не компилируется?
    А не компилируется из-за непонимания концепции эквивалентности типов. В отличие от Си, внешне одинаковые типы не эквивалентны! Для эквивалентности надо, чтобы их цепочки type A = B; вели к одному «предку». Для этого существует оператор type.
    const
      FieldSize = 10;
      MaxShips = 10;
    type
      TField = record
        cells : array [1..FieldSize, 1..FieldSize] of integer;
        nLive : array [1..MaxShips] of integer;
      end;
      TGame = class
        Field : TField;
        constructor Create(const Field : TField);  
      end;

    Возможно, эквивалентность ослабили в Delphi, не проверял. А в BP именно так.

    Возможно, вы также сделали известную ошибку начинающего дельфиста:
    var
      x : Test;
    ....
    x.Create(a, b);     // неверно!
    x := Test.Create(a, b); // верно!


    Есть одно исключение из этой эквивалентности типов.
    type
      DaInt = array of integer;
    
    procedure DoSomething1(var x : array of integer);
    procedure DoSomething2(var x : DaInt);

    Эти команды обе действуют, но не эквивалентны!

    Первое — нововведение TP7, параметр типа «открытый массив», массив любой размерности. Статический, динамический, строчка 2D-массива — всё подойдёт. Действуют Low, High и (для D4+) Length.

    Второе — нововведение D4, динамический массив, которому можно изменять длину через SetLength.
    Ответ написан
    1 комментарий
  • Как использовать кириллицу в параметрах запроса IdHTTP?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Разобраться, в какой кодировке работает сервер (обычно UTF-8).
    2. Для путей — провести преобразование (Utf8Encode вроде). Не забудь, что Delphi 7 «из коробки» неюникодная.
    3. Для доменных имён — закодировать в PunyCode. Это уже сам как-то — думаю, есть библиотеки, но точно не в коробке.
    Ответ написан
    3 комментария
  • Есть ли алгоритм определения оптимального размера посылки?

    @Mercury13
    Программист на «си с крестами» и не только
    Упаковка параллелепипедов. NP-полная задача, точное решение — перебор. Сам решал упаковку прямоугольников (т.е. в 2D) генетическим алгоритмом с переменным успехом.
    Скорее всего, у вашей службы доставки есть ящики стандартного размера — потому стоило бы приспосабливаться к этим ящикам.
    Ответ написан
    1 комментарий
  • Как запустить на выполнение exсel файл из java?

    @Mercury13
    Программист на «си с крестами» и не только
    docs.oracle.com/javase/6/docs/api/java/awt/Desktop.html

    Как работает, не проверял.
    Ответ написан
    Комментировать
  • Android и аппаратная клавиатура - покой навеки? Почему?

    @Mercury13
    Программист на «си с крестами» и не только
    Расскажу как программист о подоплёке этого. Тут есть два вопроса.
    1. Какие телодвижения должен сделать разработчик этого? Насколько это легко?
    2. И какая выгода будет от этого?

    Когда-то деловое ПО разрабатывали под конкретное разрешение экрана. Когда IBM стала делать всё новые и новые машины, сохраняя преемственность, программисты стали делать, чтобы ПО «тянулось»: проверяется за пять секунд и приносит изрядные неудобства, когда плохо реализовано.

    А вот с HiDPI вышло не так радужно. У кинескопа все разрешения были «нештатные», в отличие от одного штатного на ЖК. А ещё был неуменьшаемый предел DPI, порядка 0,2 мм, ограничивавшийся сведением лучей. Первая ОС Microsoft с поддержкой HiDPI — Windows 95 (!). Но чтобы проверить, компьютер требовалось перезагрузить. И перезагрузить ещё раз, чтобы вернуть как было. Естественно, на это подзабили и вернулись, когда уже петух клюнул — появился Яббл со своей Retina.

    Точно так же и тут. Перепробовал пару программ на KitKat. В «лисичке» Tab работает. В Яндекс-картах работает, но не видно, что выделено. Это значит: какая-то поддержка со стороны ОС есть, да и методичка Google говорит: всё должно работать на устройствах с «железной» крестовиной. Реально на это подзабили.

    Окей. Бросим пока программизм. Инвалиды. Все эти пандусы, съезды и прочее. Телодвижений от конструктора требуется куча. Выгода — разве что тем самым инвалидам. Вот и имеем то, что имеем.
    Ответ написан
  • Какими приёмами вы пользуетесь чтобы различать указатели, которые нужно освобождать, от указателей, которыми нужно только пользоваться?

    @Mercury13
    Программист на «си с крестами» и не только
    Но есть ещё один указатель, который реализует именно что единую ответственность — std::unique_ptr. Также я использую одну самоделку — но, возможно, «не умею готовить» unique_ptr.
    Ответ написан
    1 комментарий
  • Что значит Buffered?

    @Mercury13
    Программист на «си с крестами» и не только
    ДЛЯ ВВОДА-ВЫВОДА: Он читает информацию большими кусками и хранит эти куски в памяти.
    Служит для ускорения доступа, если на самом-самом высоком уровне файл надо читать по одному байту или символу, а на самом-самом низком (например, жёсткий диск) эффективнее это делать блоками по 4K.

    У читателей flush (если таковой есть; у BufferedReader, например, его нет) служит для потоков реального времени: клавиатуры, коммуникационных портов и прочего. Всё, что накопилось в буфере, сбрасывается, и считается, что на момент T поток пуст.

    Писатели, естественно, пишут данные не сразу, а когда буфер заполнится, и авария программы или перебой электричества эти данные похѣритъ. Для них команда flush физически записывает накопленные данные.

    ДЛЯ КАРТИНОК: Картинка хранится не в «аппаратно-эффективном» виде, а в таком, чтобы пользователь мог её относительно просто менять.
    Ответ написан
    Комментировать
  • Как найти минимальное время которое удовлетворяет условие?

    @Mercury13
    Программист на «си с крестами» и не только
    Если для боя с боссом нужно 0 очков — выводим 0.
    Если все миссии в сумме дают <K очков — выводим «Нельзя».
    А если нет — ничего пока не вижу, кроме продвинутого перебора.

    Сортируем миссии по соотношению «очки/время». Главное — правильно отсечь перебор: например, каждый раз линейно экстраполируем по соотношению «очки/время» лучшей доступной миссии: удастся вписаться или нет? Смотрим на сумму «хвоста»: можно ли вообще, просуммировав «хвост», получить K?
    int N, K;
    Mission missions[100];
    int bestTime = std::numeric_limits<int>::max();
    
    bool recurse(int firstMission, int currPoints, int currTime)
    {
        if (currTime >= bestTime)
            return false;
        if (currPoints >= K) {
            bestTime = currTime;
            return false;
        }
        if (firstMission >= N)
            return true;
    
        int remPoints = K - currPoints;
    
        for (int i = firstMission; i <= N; ++i) {
            bool isFirst = (i == firstMission);
            const Mission& im = missions[i];
            if (currPoints + im.tailPoints < K)           // tailPoints = сумма очков по хвосту от missions[i] до конца
                return isFirst;
            float wantedTime = currTime + remPoints * im.ratio;    // ratio = (float)m.time / m.points;
            if (wantedTime >= bestTime)
                return isFirst;
            if (recurse(i + 1, currPoints + im.points, currTime + im.time))
                return isFirst;
        }
        return false;
    }

    Миссии, как и раньше, отсортированы по ratio по возрастанию.

    И последняя моя идея, в разы ускорившая перебор, такова. Рекурсивная функция возвращает true, если весь разрешённый «хвост» безнадёжен и в нём искать больше нечего. Это происходит при таких условиях.
    1. Хвост пуст.
    2. Хвоста недостаточно, чтобы получить K очков.
    3. Простейшая экстраполяция по 1-й миссии провалилась.
    4. Уже первый рекурсивный вызов говорит: хвост безнадёжен.

    UPD2. Ну и вторая идея, которая там есть. Если хвост безнадёжен, то меньшие хвосты безнадёжны и подавно.
    Ответ написан
  • Какие обьекты удалять в деструкторе?

    @Mercury13
    Программист на «си с крестами» и не только
    После того, как отработает тело деструктора, для всех полей объекта автоматически вызываются деструкторы, в обратном порядке.
    У int и double деструкторы нулевые — но другие-то и не нужны.

    В каких случаях надо писать деструктор самим…
    1. Мы владеем каким-то ресурсом, но штатный деструктор его не уничтожает.
    • Простой (не умный) указатель и выделенная память — это хорошо описал sitev_ru .
    • Объект-блокировщик, например, мьютекса (мьютекс — примитив межпоточной синхронизации, не дающий зайти в определённые участки кода одновременно двум потокам).
    class Mutex {
    public:
      void enter();
      void leave();
    }
    
    class Lock {
    public:
      Lock(Mutex& aMutex) : mutex(aMutex) { mutex.enter(); }
      ~Lock() { mutex.leave(); }
    private:
      Mutex& mutex;
    }
    
    …
    Mutex mutex;
    { Lock lock(mutex);
      // всё, что здесь, выполняется внутри мьютекса.
      // Даже если выпадет авария, из мьютекса корректно выйдем.
    }

    2. Сложный порядок уничтожения. Односвязный список из std::unique_ptr будет работать и так, со штатным деструктором, но это чревато переполнением стека.
    3. Сложная структура владения, и при уничтожении надо автоматически отобрать объект у владельцев. По-чёрному используется в оконных фреймворках на манер VCL и Qt. Удаляем компонент — он автоматически отбирается у владельца.

    По опыту: если структуры данных выносить в отдельный объект (ну и использовать STL, где можно), 80% объектов Си++ будут с автоматическим деструктором.

    ЗЫ. По результатам прикидочных подсчётов в живом проекте, ≈150 файлов (реально файлов 219, но не все наши собственные; библиотечные деструкторы не учитывал).
    • Классов с настоящим деструктором — около 30. Большей частью системные (W32Cs — быстрый мьютекс Win32) или структуры памяти (Array1d, например). Из них в собственно проекте (не в личной библиотеке программиста) — три (!): один связан с автовладением чужой библиотекой XLSX, два — с фоновыми потоками.
    • Интерфейсов с пустым виртуальным деструктором — порядка 40.
    • И ещё деструкторы, автоматически добавленные Qt — по числу форм, ровно 20.
    • И единицы пустых деструкторов, добавленных по желанию левой пятки линкера.
    Ответ написан
    Комментировать