Ответы пользователя по тегу C++
  • Как создать экземпляр класса, реализующего интерфейс?

    Nipheris
    @Nipheris Куратор тега C++
    Жесть полная.

    class iClass {
    public:
    void method1();
    virtual method2();
    }


    Это не абстрактный класс, у абстрактного должно быть недоопределенное поведение, т.е. хотя бы один pure-virtual метод. У вас есть виртуальный метод, но для него ожидается релазация. Если бы iClass был бы абстрактным, то код
    new iClass();
    никак бы не скомпилился

    Как правильно создать экземпляр класса myClass через интерфейс iClass и использовать у этого экземпляра method3()?


    Правильно - никак, задача поставлена некорректно. Если у вас есть некоторый интерфейс, работать нужно через него. Если вам нужно создавать экземпляры различных классов-реализаций, вы должны писать new КонкретныйКласс(), а потом уже при желании, приводить к типу интерфейса. Если вам нужен method3, то вам НЕ НУЖЕН iClass, вам нужен myClass, т.к. именно В НЕМ есть method3.

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

    Nipheris
    @Nipheris Куратор тега C++
    ОФФТОП:
    1) Object obj(); - это объявление функции obj без параметров, возвращающей объект класса Object. Объявление объекта obj делается без скобок: Object obj; Да, такие синтаксические тонкости.
    2) про звездочку вам уже сказали

    ПО ДЕЛУ:
    1) Object obj;
    "компилятор-компилятор, создай-ка мне автоматическую переменную с объектом в текущей области видимости. За пределами этой области видимости она не нужна, так что можешь убить его сам, чтобы я не парился на этот счет". Компилятор и убьет, т.е. вызовет деструктор и освободит память, если уже пора.
    2) Object* obj = new Object();
    "компилятор-компилятор, создай-ка мне автоматическую переменную-указатель obj, а также ручную переменную с объектом, расположив ее в динамической памяти (вместо стека/статического сегмента в 1-м случае) и дай мне ее указатель, я его сохраню в obj. Я этот указатель потом буду далеко передавать, записывать в другие структуры и объекты, так что ты, компилятор, не парься, я сам потом попрошу уничтожить объект и освободить память, когда буду знать, что уже можно".

    Плюс первого варианта - время жизни переменной и объекта в ней совпадает с областью видимости. Вам следить за временем ее жизни не нужно, но при этом вы не сможете работать с переменной, если она уйдет из области видимости (например, грубейшая ошибка новичка - возвращать из функции указатель на локальную переменную). Следовательно, таким образом сложно создавать и использовать "долгоживущие" объекты, вроде окон или подключений к БД.
    Плюс второго варианта - вы создаете и убиваете объект абсолютно в любом месте программы, но компилятор умывает при этом руки - вы запросили память, вам ее и освобождать. В языках со сборщиком мусора это делает сложный алгоритм, который анализирует достижимость объектов в памяти из программы. В C/C++ за этим следит программист.

    Но программист может себе упростить жизнь с помощью умных указателей, добавьте себе на заметку, почитаете позже, когда разберетесь с основами.
    Ответ написан
    Комментировать
  • C++ | boost::asio + ssl?

    Nipheris
    @Nipheris Куратор тега C++
    Инструкция для Boost::ASIO: www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/...

    Использование HTTP-клиента в cpp-netlib с возможностью HTTPS-соединений: cpp-netlib.org/0.11.1/reference/http_client.html
    Ответ написан
    Комментировать
  • Как исправить ошибку LNK2019 при использовании статического метода?

    Nipheris
    @Nipheris Куратор тега C++
    Теоретически заголовочный файл в проект подключать не обязательно, это для вашего удобства (чтобы вы не теряли файлы, которые у вас есть в проекте). А вот cpp нужно полюбому, т.к. иначе у компилятора не попросят его собрать, а линковщику - не расскажут, что есть объектный файл (который получится после компиляции), который содержит реализации неких функций. Результат - линковщик ищет-ищет, и не находит релизацию метода.
    Если бы у вас был lib а не cpp, то ситуация та же самая, только без этапа компиляции. Грубо можете считать lib-файл группой собранных воедино объектных файлов. В нем уже скомпилированный код находится, поэтому его нужно передавать сразу линковщику. Для этого в ключах линкощика указываются так называемые Library Directories - директории, где искать библиотеки, а также dependencies - какие конкретно lib-файлы надо просматривать при линковке. В студии есть как глобальный список таких директорий, так и локальные настройки для каждого проекта.
    Ответ написан
    1 комментарий
  • Какие качественные open source проекты есть на github (или где еще), для изучения?

    Nipheris
    @Nipheris Куратор тега C++
    Пара больших проектов:
    https://github.com/mapnik/mapnik
    https://github.com/mapbox/mapbox-gl-native

    Большая база кода, написано аккуратно, на современных плюсах, хорошая структура проекта, есть тесты.
    Ответ написан
    1 комментарий
  • Где можно посмотреть зарезервированные имена переменных?

    Nipheris
    @Nipheris Куратор тега C++
    Ошибку выдает компилятор или линковщик?

    Если компилятор, то это довольно странно, попробуйте вывести не y1, а строку, чтобы убедиться что проблема в y1.
    Если линковщик, то вероятно у вас y1 попадает external-линковкой из другого файла. Все правильно, из безымянного неймспейса ничего экспортироваться не будет, но это не значит, что имя из этого пространства "перекроет" собой остальные. Вот если вы найдете ДРУГОЙ y1 в другой единице компиляции, и его ТОЖЕ поместите в безымянный namespace, тогда все скомпилится без проблем.
    Ответ написан
    5 комментариев
  • Является ли статическая функция С++ потокобезопасной?

    Nipheris
    @Nipheris Куратор тега C++
    С чего бы этому вызову быть потокобезопасным, если у вас в функции статические переменные?
    Выполняется ли проверка File()::exists(settingsFilePath) при каждом обращении к функции

    А почему она не должна выполняться?

    Кстати, а зачем вам две разные статические переменные set?
    Ответ написан
    Комментировать
  • Как отобразить QWidget в QML приложении?

    Nipheris
    @Nipheris Куратор тега C++
    Сам не пробовал и не проверял, но есть отличный ответ на SO.

    Пишут, что можно в Qt Quick 1, но нельзя в 2+ по причине введения графа сцены.
    Ответ написан
    Комментировать
  • QTreeView и QSqlRelationTableModel/QTableModel?

    Nipheris
    @Nipheris Куратор тега C++
    Наиболее гибким вариантом в вашем случае я считаю наследника от QAbstractItemModel. Отображать его вполне можно с помощью QTreeView.
    Ответ написан
  • Почему функция "cpy" работает со строками?

    Nipheris
    @Nipheris Куратор тега C++
    String(const char *str);//конструктор по принимаемой строке

    вот и ответ. (конструктор неявного преобразования).

    Если хотите проверить - поставьте перед этим конструктором кейворд explicit - и строка
    a.cpy("rap").print();

    должна перестать компилироваться.
    Ответ написан
    1 комментарий
  • Где логическая ошибка?

    Nipheris
    @Nipheris Куратор тега C++
    > (a-b)/2
    Проверьте, не случилось ли у вас тут целочисленного деления
    Ответ написан
    Комментировать
  • Не работает вставляется в вектор?

    Nipheris
    @Nipheris Куратор тега C++
    it < words.end();

    Старайтесь итераторы сравнивать с помощь == и !=. Сравнение на больше/меньше работает не со всеми итераторами, не стоит привыкать к нему.

    *word

    Конструктор копирования корректно определили? Даже если да, зачем такие извращения с созданием объекта сначала на куче, а потом его копирования по значению? не проще ли работать с Word на стеке или наоборот, сохранять в вектор указатель? Вы кстати еще и забываете удалить созданный на куче объект класса Word.
    Ответ написан
  • ClassName::functionName или objectName.functionName?

    Nipheris
    @Nipheris Куратор тега C++
    Отвечу без догадок.
    > Я понимаю что cout << objectName.functionName означает, что мы выводим возвращаемые данные функции , которая принадлежит объекту класса(оперирует данными объекта).
    Это хорошо соответствует истине, добавить в общем-то нечего.

    > Но вот почему мы иногда использует cout << ClassName::functionName вместо первого варианта я не могу понять...
    "::" это оператор расширения области видимости. В языке C++ различные сущности могут быть объявлены "внутри" других сущностей. Например, функции внутри классов, как в вашем случае - тогда они становятся методами. Классы, функции и переменные могут объявляться внутри namespace-ов. Классы могут объявляться внутри других классов. Классы даже могут объявляться внутри определения функций.
    Во всех этих и других случаях, можно говорить о двух именах сущности - коротком, которое уникально в рамках родительской сущности, и о полном, по которому к сущности можно обратиться из любого места в программе. Например,

    namespace MyLibrary {
        namespace UI {
            class Widget {
             ///
            };
            class Controller {
            private:
                Widget *widget; // Здесь Widget будет видно по короткому имени
            };
        }
    }
    
    namespace App {
      MyLibrary::UI::Widget *w; // А вот здесь уже нужно использовать полное
    }

    Это правило касается всего - и классов, и функций, и переменных. И вот как раз для построения полного имени и нужен оператор "::". Грубо говоря, он "открывает" указанную вами сущность и после него вы можете указать имя вложенной сущности. MyLibrary::UI::Widget - открыли неймспейс MyLibrary, в нем взяли и открыли неймспейс UI, в нем взяли сущность Widget. В нашем случае Widget это класс, поэтому мы можем использовать его в качестве типа, например объявить указатель на его объект. Если бы у нас была переменная mainWidget в том же UI, мы бы могли написать так:
    MyLibrary::UI::mainWidget = new MyLibrary::UI::Widget(); // создали объект виджета в куче и поместили указатель на него в переменную-указатель


    С точкой тут вот в чем дело. По умолчанию функции внутри классов считаются методами ОБЪЕКТА (экземпляра этого класса), и, как вы правильно сказали, оперируют с данными объекта, а значит они НЕ МОГУТ быть вызваны без указания того объекта. Иными словами, они не самостоятельны, для их вызова всегда нужно указывать объект, с которым метод будет работать. Можете представлять себе, что у всех методов экземпляра есть неявный параметр this, который, хоть и не пишется в списке, тем не менее всегда присутствует, и его значение надо задавать. Оператор точка - это и есть способ задать значение "this" - т.е. указать, для какого объекта вызывается метод.

    Совсем другое дело - static-методы. По сути это самостоятельные функции, просто объявлены внутри класса, и имеют доступ к его private-сущностям. Поэтому для их вызова достаточно указать полное имя, используя оператор "::", например Widget::create или MyLibrary::UI:Widget::create, что в принципе одно и то же. Более полная запись нужна, когда вы находитесь в другой области видимости, и просто "не видите" нужный идентификатор. Или же если у вас в текущий области НЕСКОЛЬКО идентификаторов с одинаковым именем (конфликт имен), и компилятору необходимо однозначно понять, о какой сущности вы говорите.

    Касательно случая, который вам непонятен - про использование :: в составном имени метода - тут все просто. Т.к. вы собираетесь не ВЫЗВАТЬ метод, а дать его ОПРЕДЕЛЕНИЕ, вам не нужна точка, т.к. точка это синтаксис вызова метода для конкретного объекта. Вам лишь нужно построить ПОЛНОЕ имя для метода, находящегося внутри класса, чтобы компилятор понял, ЧЕЙ код вы будете писать в фигурных скобках. А полное имя строится с помощью двоеточия, поэтому и получается ClassName::methodName { код метода }. Просто methodName вы написать не можете, т.к. в cpp-файле вы находитесь УЖЕ НЕ ВНУТРИ определения КЛАССА, и компилятор посчитает, что вы объявляете и описываете совсем другую, свободную функцию methodName, совершенно не имеющую отношения к методу methodName в классе ClassName.

    Есть некоторые интересные особенности у "::", например когда слева от него ничего нет. Это значит, что вы обращаетесь к глобальной области видимости. Это позволяет, например, различать глобальную функцию, и функцию с таким же именем, определенную в вашей области видимости. Это можно в приличной книжке все прочесть.

    Если остались вопросы - задавайте. Запомнить проще всего так: точка - это обращение к члену объекта структуры или вызов метода (т.е. слева от точки всегда стоит объект), а "::" - это способ составления ИМЕНИ какого-либо элемента вашей программы - класса, функции, метода, переменной и т.д.
    Ответ написан
    1 комментарий
  • Область применения C, C++, C#?

    Nipheris
    @Nipheris Куратор тега C++
    > Хотел задать вопрос к опытным программистам.

    Ну раз опытным, то думаю разумно поделиться своим опытом. Итак, в чем участвовал за последние 5-6 лет, где были плюсы или шарпы. Три места работы, три команды:
    - десктопная софтинка наподобие 2ГИС - база данных услуг и предприятий с привязкой на карту. C#, карту отрисовывали сами с помощью Direct3D;
    - TCP-сервер GPS-трекеров (такие фиговины, отправляют по GRPS/SMS свои текущие GPS/ГЛОНАСС координаты + всякие плюшки для транспорта - уровень топлива, скорость движения и т.д. - зависит от модели). C++ и обыкновенные сокеты. Сервер небольшой, принимал именно данные от трекеров и писал в базу. Отображалось все на обыкновенном сайте с PHP в бэкенде.
    - веб-сервис, принимающий платежи по WebMoney Merchant и поддерживающий балансы на счетах клиентов. Открывал урл для серверов вебмани, плюс давал простейшие отчеты (проведена транзакция или нет, текущий баланс, транзакции на списание). C# (WCF), данные писались в PostgreSQL.
    - десктопное приложение для проходной - по отпечаткам пальцев входящих/уходящих сотрудников регистрировалось их время пребывания на предприятии. Ну и разумеется - отчеты, агрегации (время отработанное за неделю, опоздания, переработки и т.д.). C# + некоторые части на С++, соединяющие драйвер сканера отпечатков и библиотеку их распознавания по образцам.
    - здоровенное декстопное приложение на C++ + Qt, трейдинговый терминал (более 10к файлов исходников, команда из 30+ человек);
    - довольно объемный ГИС-проект на C#, клиент десктопный (WPF+SharpMap), серверная часть - ASP.NET WebAPI (JSON API).
    - планируется новый ГИС-проект с клиентом уже на C++ и Qt, т.к. существует нереально крутые рендереры на OpenGL от MapBox (тык), а большинству участников текущего проекта плевать - C++ или C#. Qt сейчас развивается очень серьезно, поэтому на сегодняшний день он выровнялся с C#+WPF, а т.к. рендерер на плюсах - то и клиента будем писать на плюсах. На сервере по-прежнему ASP.NET, вероятно новой версии (пока начнем писать, должна успеть выйти в релиз); тех, кто считает, что с ним "сложно в вебе" - аргументы в студию;
    - небольшой сайд-проект - рендер сложного 3D объекта для внедрения в рекламный ролик новой фантастической книги. С++ и OpenGL, написано быстро, дешево и сердито, отрисовано покадрово в PNG-шки, смонтировано в After Effects, все довольны.

    Вывод: поверьте, если инструмент подчиняется вам, то вам открыто много способов решения различных задач. Конечно, для Web-бэкенда C++ будет очень странным выбором, но лично у меня хватает задач и без бесконечных мелких сайтиков.

    Послесловие: безусловно, начинать лучше с того же Паскаля (да, я серьезно, отличный язык для обучения, дисциплинирует, и при этом не скрывает машину от программиста). Но если уж выучите С++, или хотя бы Си, то остальные языки после него будут как игрушки с наворотами. Конечно, для этого вам уже надо знать, что вы хотите стать программистом. Если еще не уверены - лучше попробовать на более простом языке, иначе перегорите не дойдя и до середины.
    Ответ написан
    12 комментариев
  • Как добавить WS_BORDER чужому окну?

    Nipheris
    @Nipheris Куратор тега C++
    Судя по информации о стилях окна в MSDN, для уже существующего окна этот стиль добавить/удалить невозможно.
    Ответ написан
  • Для чего же нужны указатели?

    Nipheris
    @Nipheris Куратор тега C++
    Ну и я тогда тоже попробую.

    Посмотрим на вопрос с другой стороны.
    Переменная - это контейнер для значения. Что нам вообще нужно для работы с переменной? Что нам нужно, чтобы считать/записать значение из/в нее? Нам нужны тип данных и адрес в памяти.
    Тип данных - отдельная история, оставим пока его в стороне. Скажем только, что он определяет допустимые операции со значением и количество памяти, необходимое для хранения значения.
    Поговорим об адресе. В языках, где доступна прямая работа с памятью, любая переменная имеет свой адрес в памяти. Также, в чуть более узком смысле, переменная находится в такой области памяти, в которую разрешена запись. Не имеет особого значения, какой структурой данных управляется эта память - стеком или кучей - важно, чтобы на момент использования эта память была доступна.
    Корректный адрес в памяти - это и уникальный "ключ" переменной, ее отличительная черта. Работая с переменными, программист на низкоуровневом языке неизбежно работает с адресами.
    Другой вопрос - это способ работы с адресом переменной. Когда вы создаете обычную локальную переменную, в работе с ней принимает участие компилятор. Когда вы пишете int a, компилятор (если не вдаваться в детали) размещает у себя в таблице идентификаторов пару: (имя_переменной, адрес_в_памяти). Обычная локальная переменная "а" (еще ее называют "автоматическая переменная") - способ создания переменной средствами компилятора. Компилятор освободит память, занимаемую этой переменной, когда она уйдет из области видимости. Однако, пока вам точно известно, что эта переменная "живет", вы можете совершенно спокойно получить ее адрес с помощью операции & - компилятор отдаст вам его из своей таблицы идентификаторов.

    Но "переменные" в широком смысле можно создавать не только средствами компилятора, но и вручную с помощью malloc (Си) или new (С++). Эти динамические переменные живут столько, сколько вам нужно - вы их создаете, вам их и уничтожать. Об этих переменных компилятор ничего не знает, т.к. вы создаете их динамически во время выполнения программы. Для доступа к этим переменным вам также нужен адрес, но у компилятора его не попросишь: поэтому необходимо самому сохранять те адреса, что вернули вам функция malloc или оператор new. Этот адрес вы можете сохранить в ДРУГОЙ переменной, и такая переменная, хранящая адреса - и есть указатель (кроме того, если указатель не бестиповый (void*) то его тип (float*) еще и подсказывает нам тип переменной, на которую он указывает (float)).

    Очень важно, что в указатель можно сохранить адрес ЛЮБОЙ переменной - как автоматической, которую вам создал компилятор, так и "ручной" - которую создали ВЫ с помощью malloc/new. И передать, например, этот адрес в функцию. Фактически, в языке Си указатели это и есть способ передачи САМОЙ ПЕРЕМЕННОЙ в функцию, а не ее ЗНАЧЕНИЯ на момент вызова. В C++ есть еще ссылки, но это отдельная история (ссылки - это указатель, "обернутый" в обычный идентификатор), по ним задайте отдельный вопрос.

    если можно напрямую данной переменной присвоить новое значение.

    Как раз таки "напрямую" вы не присвоите, т.к. у вас не может быть в одной области видимости ВСЕХ переменных, имеющихся в программе. Хотя бы потому, что у вас могут быть динамические переменные, и единственный способ работы с ними - работать через указатель.

    Если вы никогда не работали с динамической памятью, вы можете спросить, какой смысл в "ручных" переменных, если для хранения их адреса нужна парная переменная-указатель? А вся фишка в том, что адрес динамически созданной переменной также можно хранить в динамически созданном указателе. Тут-то и открывается вся бесконечная свобода построения динамических структур данных. Если вы не слышали про связный список, то самое время почитать хорошую книгу с примерами. В одном ответе этого не расскажешь, это основы программирования.
    Ответ написан
    Комментировать
  • C++, объявления функций и их расположение в памяти, как обстоят дела?

    Nipheris
    @Nipheris Куратор тега C++
    Является ли пример 2 примером того, как лучше не делать в больших проектах?


    Большинство компиляторов будут инлайнить функцию в этом случае.

    Разница очень простая - в первом случае вы сможете вынести реализацию в отдельную единицу компиляции, а во втором - нет. Зачем это делать - читайте внимательно про compilation unit. Особенно, если вы пришли из языка, где такой задачи, как разделение include-файлов и файлов реализации нет в принципе.
    Конечно, есть случаи когда вынести реализацию в отдельный cpp-шник невозможно. Стандартный пример - шаблонные классы. Для них реализации всех функций все равно придется указывать в файле, который будет инклудиться. Поэтому особой разницы между 1 и 2 в этом случае нет.

    это как-то связано с компилятором и тем, как он выделяет под функцию память.

    Тогда почитайте про inlining функций, это видимо то, что вы слышали.
    Ответ написан
    1 комментарий
  • Как построить сбалансированное бинарное дерева поиска, в котором данные хранятся в листах?

    Nipheris
    @Nipheris Куратор тега C++
    Если хотите хранить данные только в листах, храните в нелистовых вершинах интервал. Например, в дереве на рисунке вместо нелистовой вершины 37 храните интервал [30, 49]. Подобная идея используется в https://en.wikipedia.org/wiki/R-tree - почитайте про него тоже, если вам нужно для точек. Это как раз дерево для пространственной индексации.
    Ответ написан
  • Как научиться писать системные программы для компьютера?

    Nipheris
    @Nipheris Куратор тега C++
    Автору вопроса для начала посоветую понять и осознать, что C++ это такой язык, где стандартная библиотека обеспечивает лишь базовые нужды, а все остальное обеспечивается бесчисленным количеством сторонних библиотек (как Сишных, так и плюсовых), написанных на данный момент.
    Безусловно, WinAPI это системный интерфейс, также как и стандарт POSIX, но я не понимаю почему тут все его начали советовать. Правильно заметил Дмитрий Ковальский - для записи в файл скорее всего будет достаточно файловых потоков в стандартной библиотеке. Для многого другого хватит Буста. Именно с них и надо начинать. Использовать непосредственно винапи без веской причины сегодня не стоит. Лучше сначала поискать кроссплатформенные библиотеки.
    Ответ написан
    5 комментариев
  • Почему происходит выход за пределы массива?

    Nipheris
    @Nipheris Куратор тега C++
    // cats[9][2] // queue имеет 18 чисел

    у вас cats это массив из двух string-ов, но вы его индексируете от 0 до 8:
    cats[i][0]

    вы точно понимаете, что пишете? не вижу, чем бы вам тут мешала строгая типизация, на js это было бы также непонятно что
    Ответ написан