Ответы пользователя по тегу C++
  • Какую архитектуру выбрать для программы на Qt?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Я бы посоветовал:
    • Не делать главный класс окна божественным объектом. Его нужно разделить на контролы-модули с их собственной кастомной логикой, валидацией и прочим, а связать между собой - сигналами и слотами.
    • Отделять бизнес-логику от интерфейса. Для чего использовать внедрение зависимостей и абстрактные (чисто виртуальные) классы для декларирования API.


    Допустим, парсером парсится конфиг в объект настроек, эти настройки передаются в фабрику, которая с оглядкой на них будет выдавать правильно сконфигурированные объекты по запросу. Ссылка на фабрику передается в класс окна, откуда добываются зависимости и передаются дальше контролам по ссылке. Контролы, получая зависимость (допустим, через конструктор), привязываются к ней через сигналы/слоты или непосредственно вызывают виртуальные методы.

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

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Так, как вы написали в комментариях - не прокатит для сложных объектов, вроде Sport.

    Во-первых, вы записываете и читаете txt-файл в двоичном формате. Вы представляете вообще, как должен выглядеть файл, содержащий данные вашего типа?

    Во-вторых, у вас внутри объекта есть динамические члены (способные изменять свои размеры) - vector и string. Благодаря этому объект имеет многомерное представление в памяти и вам необходимо придумать, как спроецировать его на "плоскую" память, записать его почленно. Если с char, int, float все очевидно (их размеры известены на этапе компиляции: sizeof(char), sizeof(int), sizeof(float)), то для string, например, нужно сначала сохранить его размер (чтобы потом знать, сколько читать из файла), а потом записать содержимое. С vector - аналогично, за тем исключением, что это тот же Sport и вам нужно вызвать функцию сохранения для каждого ребенка текущего объекта (и для их детей, т.е. рекуррентно обойти дерево владения).

    Чтение производить в обратном порядке.
    Ответ написан
    Комментировать
  • Поясните смысл статической функции вне класса в C++?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Семантика совершенно другая. Область видимости статической функции ограничивается единицей трансляции в которой она определяется.
    Своего рода инкапсуляция в C.
    Ответ написан
    7 комментариев
  • Зачем использовать бессмысленные сеттеры/геттеры?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Когда вам потребуется добавить в сеттер валидацию, а в геттер - кэширование, то не придется менять вызовы в клиентском коде. К тому же, аксессоры можно делать виртуальными.

    IDE прекрасно генерирует такой код, равно как и он сам легко оборачивается в макрос. Многие компиляторы так же имеют языковые расширения, позволяющие реализовать properties в C++.
    Ответ написан
    Комментировать
  • Переопределение типа class, в чём проблема?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Или это VS гребет?

    Очень вряд ли)

    1. Не нужно инклудить файлы исходного кода (*.cpp). Объединением объектных файлов в один исполняемый занимается линкер, список файлов для него у вас в *.cproj-файле и так есть.
    2. Содержимое хэдеров нужно оборачивать в include guards или в начале писать директиву компилятора #pragma once (это понимают все популярные современные компиляторы). Такое действие не даст несколько раз заинклудить один и тот же хэдер в одной единице трансляции.
    Ответ написан
    5 комментариев
  • Почему элемент вектора не присваивается переменной?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Наверное, ошибка в том, что не "который встречается в нём N/2 раз", а "как минимум N/2 раз, где N - количество уникальных элементов"? Т.е. у вас ошибка в строгом сравнении.
    if((vec.size()-1)/2.0 == mycount ) // <<------

    Вариант с std::map<char, int>
    #include <iostream>
    #include <fstream>
    #include <map>
    
    using namespace std;
    
    int main(int argc, char* argv[]) {
        ifstream input("base.txt");    
        map<char, int> charFrequencyMap;
    
        cout << "Source vector: ";
        while( !input.eof() ) {
            char sym;
            input >> sym;
            cout << sym << " | ";
            charFrequencyMap[sym]++;
        }
        input.close();
        cout << endl;
    
        int charsCount = charFrequencyMap.size();
    
        cout << "Leading elements: ";
        for (pair<char, int> item : charFrequencyMap) {
            bool isLeading = item.second >= charsCount / 2.;
            if ( isLeading ) {
                cout << item.first << " | ";
            }
        }
        cout << endl;
    
        return 0;
    }
    Ответ написан
    5 комментариев
  • Как в wxWidget корректно отображать кириллицу?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Сконвертируйте исходники с литералами в UTF-8 (любым текстовым редактором).
    Ответ написан
  • Есть ли в С++ аналог #region из C#?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    В C++ принято отделять объявления от определений, поэтому острой необходимости в таких штуках нет.
    Ответ написан
    Комментировать
  • Как подключить свой .h файл?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Это не ошибка, а warning. Очевидно, что много воды утекло с тех времен, когда Страуструп писал это в книжке.
    Компилятор говорит, что у вас есть три варианта:
    1. не обращать внимание на предупреждение
    2. задефайнить _SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
    3. не использовать hash_map


    На MSDN вы могли бы нагуглить следующее:

    This API is obsolete. The alternative is unordered_map Class.
    Ответ написан
  • Что происходит при присвоении нового значение переменнной, хранящей объект?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Происходит ли уничтожение объекта, хранящегося в a?

    Перечитал вопрос) Если оператор не перегружен, то данные будут затёрты, сам объект a будет бинарной копией b. Если перегружен, то произойдет лишь то, что там описано.

    Объект, хранящийся в b, копируется побитово или с использованием конструктора копирования?

    С использованием оператора копирующего присваивания =3 По умолчанию он копирует объект побитово (так же поступает умолчательный конструктор копирования, кстати).

    удалось выполнить присваивание без наличия соотв. конструктора.

    За вас дефолтные реализации дописал компилятор.
    Ответ написан
    7 комментариев
  • Есть ли парсер для header файлов(С++)?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Сейчас популярный подход - собирать сами типы уже с метаданными с помощью препроцессинга (Qt с его плагинами) или шаблонов (грибные фантазии, вроде этой). Кое-что можно узнать из type traits современных стандартов.

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

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Важно понимать, что MFC - это тонкая ОО-обертка над WinAPI, которая оживает за счет виззардов и кодогенерации. Кастомность порой там дается слишком большими усилиями. Но если вашей целью является разработка несложных интерфейсов и тесная интеграция с системой - это неплохой выбор. Но серьезно готовиться и идти на рынок с этим, как с основным инструментом, вряд ли стоит.

    многие используют C# и Java.

    Java едва ли чаще используется для десктопа, чем MFC. А вот C# сейчас цветет бурным цветом. Хотя часто встречается тандем из бизнес-логики на C++ и интерфейса на С#.

    З.Ы. Поиск по сайтам с вакансиями и банальное сравнение количества вакансий для различных технологий скажут вам больше, чем юзеры тостера, которым либо повезло, либо не повезло столкнуться с этой потрясающей технологией от Microsoft.
    Ответ написан
    Комментировать
  • Самый простой способ вызова функции в QT?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Наследование стоит применять тогда, когда вам нужен доступ к состоянию и детальным событиям. Для обработки нажатия достаточно прицепить ваш обработчик на сигнал QPushButton::clicked:

    auto btn = new QPushButton("click me!");
    
    QObject::connect(btn, &QPushButton::clicked, []() { 
    // создание диалога и вызов его в модальном режиме (через QDialog::exec)
    });
    //-----------------------------------
    void myFoo() { }
    
    auto btn = new QPushButton("click me!");
    QObject::connect(btn, &QPushButton::clicked, myFoo);

    UPD: В вашем случае можно сделать примерно так:
    auto btn = new QPushButton("click me!");
    
    QObject::connect(btn, &QPushButton::clicked, []() { 
        ::updateTable(table);
    });
    Ответ написан
  • Как лучше реализовать цепочку вызовов действий одного класса?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Это называется "fluent interface" или "method chaining".

    Все зависит от того, что вам нужно:

    В jQuery/JS или LINQ/C# возвращаются именно новые объекты.

    Хотя в том же EntityFramework есть Fluent API, где методы вызываются на некотором объекте контекста, который настраивается такой цепочкой. В этом случае лучше возвращать ссылку, как это принято делать при перегрузке операторов.
    Ответ написан
    1 комментарий
  • Как правильно выводить информацию из QVector?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Откройте для себя Model/View в Qt. В частности, ваша задачка с каталогами описана здесь.

    Вкратце: у вас есть модель, в котором вы связываете ваш способ хранения (QVector в вашем случае) со стандарным интерфейсомQAbstractItemModel (или его потомки QAbstractListModel, QAbstractTableModel или QAbstractTreeModel), который можно передать в один из стандартных вью-виджетов (QListView, QTableView, QTreeView). Реализовав интерфейс (переопределив виртуальные методы data и setData для role==Qt::DisplayRole и Qt::EditRole), вы можете просто изменять состояние модели, а вью-виджеты подхватят изменения автоматически.

    Кроме того, есть еще такая штука - QDataWidgetMapper. Он умеет привязывать виджеты (даже обычные, не View) к определенным полям в модели.
    Пример

    Пример объемный, но минимально полный, содержащий всю необходимую инфраструктуру. Допустим, мы хотим создать редактор для некоторых статей (блога?). Нам нужен простой класс Article, чтобы собрать в кучу данные о каждой отдельной статье:

    class Article
    {
    public:
        Article(int id, const QString& title, const QString& content) 
            : _id(id), _title(title), _content(content) {}
    
        int id() const { return _id; } 
        void setId(int id) { _id = id; }
    
        QString title() const { return _title; } 
        void setTitle(const QString& title) { _title = title; }
    
        QString content() const { return _content; }
        void setContent(const QString& content) { _content = content; }
    
    private:
        int     _id;
        QString _title;
        QString _content;
    };

    Для него напишем модель хранения этих статей - ArticlesModel, содержащей массив статей и методы для их добавления/удаления. Кроме того, для нее мы реализуем стандартный интерфейс QAbstractTableModel (методы rowCount, columnCount, data и setData), чтобы виджеты знали, как оттуда брать и как обновлять данные.

    class ArticlesModel :
            public QAbstractTableModel
    {
    public:
        ArticlesModel(QObject *parent=nullptr);
    
        void addArticle(const Article& art);
        void removeArticle(int id);
    
        //QAbstractTableModel interface implamentation
        int rowCount(const QModelIndex &/*parent*/) const override;
        int columnCount(const QModelIndex &/*parent*/) const override;
    
        bool data(QModelIndex& index, const QVariant &value, int role) const override;
        QVariant setData(QModelIndex& index, int role) override;
        
    private:
        std::vector<Article> _articles;
    };
    
    // =================================================================
    
    ArticlesModel(QObject *parent/*=nullptr*/) 
        : QAbstractTableModel(parent)
    {
    }
    
    void ArticlesModel::addArticle(const Article& art) {
        _articles.push_back(art);
    }
    void ArticlesModel::removeArticle(int id) {
        auto found = std::find_if(begin(_articles), end(_articles), 
                                [id](const Article& art) {
                                    art.id() == id;
                                });
        if (found != std::end(_articles)) {
            _articles.erase(artIt);
        }
    }
    
    
    int ArticlesModel::rowCount(const QModelIndex &/*parent*/) const override {
        return _articles.size();
    }
    int ArticlesModel::columnCount(const QModelIndex &/*parent*/) const override {
        return 2;
    }
    
    bool ArticlesModel::data(QModelIndex& index, const QVariant &value, int role) const override {
        if (!index.isValid) return QVariant();
    
        if (role == Qt::EditRole) {
            switch (column) {
                case 0: 
                    QVariant(_articles[index.row()].setTitle(value.toString()));
                    return true; 
                case 1:
                    QVariant(_articles[index.row()].setContent(value.toString()));
                    return true; 
            }
        }
        return false;
    }
    
    QVariant ArticlesModel::setData(QModelIndex& index, int role) override {
        if (!index.isValid) return QVariant();
    
        if (role == Qt::DisplayRole || role == Qt::EditRole) {
            switch (column) {
                case 0: return QVariant(_articles[index.row()].title()); 
                case 1: return QVariant(_articles[index.row()].content());
            }
        }
        return QVariant();
    }


    Тогда можно сделать так:

    // this - это какой-то родительский виджет
    
    auto titleEditor = new QLineEdit(this);
    auto contentEditor = new QTextEdit(this);
    
    QAbstractTableModel *model = new ArticlesModel(this);
    model->addArticle(Article(1, "Hello title", "Hello content"));
    model->addArticle(Article(2, "Another title", "Another content"));
    
    auto mapper = new QDataWidgetMapper(this);
    mapper->setModel(model);
    mapper->addMapping(titleEditor, 0);
    mapper->addMapping(contentEditor, 1);
    mapper->toFirst(); // Переключаемся на первый элемент модели


    Теперь мы можем добавлять/удалять статьи в model->add/removeArticle(const Article&), переходить с заметки на заметку с помощью mapper->setCurrentIndex(int index), менять содержимое заметок в редакторе.

    Можно добавить QListView, так же передать туда модель через setModel() и связать сигнал изменения текущего элемента списка с тем же действием mapper, добавить пару кнопок для добавления/удаления заметок и мы получим полноценный редактор.
    Ответ написан
    Комментировать
  • Есть ли в природе актуальная литература по C++?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    #include "stdafx.h"вроде должен быть первым в файле.
    В студии вам пока лучше создавать пустой проект, чтобы подобные штуки не мешали.

    c8ea5745f7ad47b481e100046e1e27b2.png

    Учитывая, что C++ поддерживает обратную совместимость, то любая, более или менее современная книжка (начиная с C++03, т.е. 2003 года) для вас будет актуальна. Да и вообще, стоило бы поискать K&R и овладевать Си.
    Ответ написан
    3 комментария
  • Что должен знать Junior C++ программист на данный момент?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Все на свете, причем еще и зависит от рода деятельности. Можно не слишком глубоко, но в общих чертах знать куда идти, в случае чего. Из популярного - STL, WinAPI, COM, Boost, кое-где Qt. Увы, но порог вхождения там высок, да еще и необходимо "затачиваться" на конкретную вакансию. Ибо embedded, системное и прикладное программирование - это разные вещи, которые требуют различных знаний.

    Мне кажется, что в этой Спарте нет джуниоров, там сразу хорошие миддлы =)
    Ответ написан
    Комментировать
  • Почему не работает what() const?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Неэкземплярные функции нельзя помечать const, потому что этот модификатор относится к состоянию, которого обычная функция не имеет.

    FYI: "сахарные" лямбды могут иметь состояние (замыкание), но фактически, если лямбда захватывает переменную, то это уже не функция, а функтор, т.е. объект.

    Да и, кстати
    err при выходе из функции what в тот же момент умирает. По Стандарту срок жизни локальных переменных может продлеваться лишь до смерти ближайшей константной ссылки на них. Срок жизни других данных не гарантируется после покидания скоупа. Мне кажется, здесь имеет место еще и компиляторозависимое колдунство.
    Ответ написан