Страницы

Поиск по вопросам

вторник, 26 ноября 2019 г.

Микросервисная архитектура Java


Посредством чего или с помощью чего взаимодействует один микросервис с другим?
    


Ответы

Ответ 1



Стоит добавить, что поскольку каждый каноничный микросервис -- это отдельный процесс то теоретически для взаимодействия сервисов может использоваться что угодно из списк вариантов межпроцессорного взаимодействия. И хотя большинство способов взаимодействия сводятся к HTTP и очереди сообщений, сервисы могут "взаимодействовать" хоть через файловую систему. Например, может существовать сервис, который мониторит таблицу БД и выполняет определенную задачу с новыми/обновленными записями. получается суть микросервисной архитектуре - это создание множества rest-сервисов, каждый из которых выполняет только одну конкретную задачу? Во-первых, просто сервисов. REST -- это уже деталь имплементации. К тому же, у сервиса вообще может не быть никакого API. Во-вторых, создание множества сервисов -- это, конечно же, способ достижения цели А суть микросервисной архитектуры заключается в следовании принципу единой обязанности. И уже отсюда вытекает все остальное: способы реализации, плюсы и минусы.

Ответ 2



Существует множество различных технологий взаимодействия одного микросервиса с другим Сервисы могут использовать синхронные механизмы связи на основе запроса/ответа, таки как REST или Thrift на основе HTTP. В качестве альтернативы они могут использовать асинхронны коммуникационные механизмы на основе сообщений, такие как AMQP или STOMP. Существуют также различные форматы сообщений. Сервисы могут использовать удобочитаемые текстовые форматы, такие как JSON или XML. Кроме того, они могут использовать двоичный формат (который более эффективен), такой как Avro или Protocol Buffers.

SimsCity 4 и Cities: SkyLine. Население


Все видели в SimCity и Cities:Skyline и подобных им, что в городе можно клацнут
по пешеходу / машине и вам высветится интерфейс, в котором есть: Имя персонажа, место жительства, место работы и куда он направляется.
Насколько я понимаю, эти жители, просто объекты одного, а может и нескольких классов
и создать такое же не тяжело. А когда города под 100к человек??.. Так вот вопрос к опытным программистам: мне кажется это очень "тяжелая" для ресурсов ноша. 100к + объектов? и нормальные системные требования? быть не может..
В чем подвох? Или лучше сказать моя недальновидность. 
    


Ответы

Ответ 1



То, что в городе 100к объектов, не значит, что создаются объекты для них всех. 100к объектов, которые, скажем, весят по 200 байт - это 20 мегабайт. Вместится. Проблемы правда, будут с GC, скорей всего. Но, думаю, можно настройки generations подкрутить так, чтоб не было больших фризов при сборке. По существу же, сомнительно, что все 100к объектов всегда в памяти. Я как минимум несколько способов вижу оптимизации: 1) Разбиение на зоны/локации. Грузить объекты только в рамках этой зоны. Вспомнит старые игры, при переходах между локациями было окно загрузки. Если мне память не изменяет, то в той же WoW'ке в своё время при переходах между локациями оно было. Это сейчас уже они оптимизировали, что не приходится ждать и создаётся иллюзия бесшовного мира. 2) Использовать пулы объектов. Чтоб не грузить лишний раз GC, да и просто, чтоб н тратить ресурсы на создание объектов, используются пулы. Скажем, изначально создат пул из 100 объектов-пустышек. По мере надобности заполнять их свойства нужными данным (когда человек/здание/машина в зоне видимости). Когда объект выходит из зоны видимости, очищать объект и запихивать назад в пул. Потом этот объект из пула уже возьмёт другой персонаж игрового мира. Я такие пулы использовал для объектов, которые часто создаются: пули, заклинания и т.п. Я могу привести код из текущего нашего проекта. Я там использую org.apache.commons.pool2 для создания пула подключений к базе данных: Например, метод, который грузит мета данные для сервиса из базы: public void loadMetaInfo(){ LOG.debug("load meta info"); DB db = null; try{ db = mDB.getResource(); // получаем подключение из пула String reqId = getRequestId(); db.send(new CommandRequest(Command.INFO.toString(), reqId), this); mRequests.put(reqId, new CacheItem("load info")); } catch(Exception ex){ LOG.error("loadMetaInfo: db_problem"); } finally{ if(db != null) db.close(); // после окончания работы возвращаем объект в пул, // чтоб им могли воспользоваться другие клиенты } } 3) Как заметили в комментариях, для оптимизация работы с памятью можно предотвращат создание экземпляров классов, имеющих общую сущность, используя паттерн Приспособлене (Flyweight pattern). Почитать про него (да и про многие другие паттерны) можно на этом сайте. По сути, это похоже на п.2. Типичный пример использования этого шаблона - Buffer pools. Паттерн Приспособленец (Flyweight) Это структурный шаблон проектирования, позволяющий использовать разделяемые объект сразу в нескольких контекстах. Данный паттерн используется преимущественно для оптимизации работы с памятью. Скажем, если мы хотим написать редактор текстовый. Для простоты положим, что используютс только буквы русского алфавита. Создавать объект на каждую букву - накладно очень. Можн определить словарь, состоящий из 33 объектов, которые мы будет переиспользовать. В итоге, к примеру, для текста из 330 букв мы уменьшили число создаваемых объектов в 10 раз. Если речь о книге, то там речь уже на тысячи идёт. Ключевым моментом является разделение состояния на внутренне и внешнее. Внутренне состояние от контекста не зависит. В примере с символами внутреннее состояние описывается кодом символа из таблицы кодировки. Так как внутреннее состояние не зависит от контекста, то оно может быть разделяемым и поэтому выносится в разделяемые объекты. Внешнее состояние зависит от контекста и является изменчивым. В применении к символа внешнее состояние может представлять положение символа на странице (строка + колонка). То есть код символа может быть использован многими символами, тогда как положение на странице будет для каждого символа индивидуально. Применительно к вашему вопросу. У каждого объекта внутренним состоянием может задаватьс размер объекта, его материал, какие-то физические величины, параметры коллайдера. Внешнее же состояние: имя персонажа, место жительства, место работы и куда он направляется. Можно показать принцип работы UML диаграммой с Wiki: Применительно к вашему вопросу накидаю по-быстрому примерную реализацию на Java Проверить код сейчас нет возможности, но, как мне кажется, главное - уловить суть. Скажем, у нас персонажи двух типов: толстые и худые. Тогда будет что-то такое. Базовый класс, определяющий объект-персонаж: public abstract class Character{ protected String mTitle; protected String getTitle() { return mTitle; } protected void setTitle(String mTitle) { this.mTitle = mTitle; } protected int getWidth() { return mWidth; } protected void setWidth(int mWidth) { this.mWidth = mWidth; } protected int getHeight() { return wHeight; } protected void setHeight(int wHeight) { this.wHeight = wHeight; } protected int mWidth; protected int wHeight; public abstract void printCharacter(); } Две его реализации: public class FatCharacter extends Character { public FatCharacter(){ setTitle("Я толстяк"); setWidth(20); setHeight(40); } @Override public void printCharacter() { System.out.println("Title: " + getTitle() + ", width:" + getWidth() + " height: " + getHeight()); } } public class ThinCharacter extends Character { public ThinCharacter(){ setTitle("Я худой"); setWidth(10); setHeight(40); } @Override public void printCharacter() { System.out.println("Title: " + getTitle() + ", width:" + getWidth() + " height: " + getHeight()); } } Фабрика, собственно, которая отвечает за создание объектов: public class FlyweightFactory { public static enum Code{ Thin, Fat}; private Map mCharacters = new EnumMap(Code.class); public Character getCharacter(Code characterCode){ Character character = mCharacters.get(characterCode); // если такого объекта ещё нету, то создаём if (character == null){ switch (characterCode){ case Fat : character = new FatCharacter(); break; case Thin : character = new ThinCharacter(); break; } // добавляем в пул mCharacters.put(characterCode, character); } return character; } } Использовать так: Character character = factory.getCharacter(Code.Fat); character.printCharacter();

Когда может пригодится пространство имен System.Reflection.Emit?


Когда может пригодится пространство имен System.Reflection.Emit ?

Вроде, в .NET предусмотрены классы, которые позволяют генерировать динамически сборки на высокоуровневом языке.

Зачем погружаться в низкий уровень?
    


Ответы

Ответ 1



Reflection.Emit — это для тех, кто хочет создавать динамические сборки (а не добавлять что-то в существующие) на чистом IL (хардкор короче). Подробнее ниже. А может вам надо что-то другое и желательно попроще? Если вам просто нужен динамический тип, может вам просто подойдёт dynamic или ExpandoObject? И никакой генерации кода, правда о проверке на этапе компиляции можно забыть. Вам нужен метод (может также их группа), который будет принимать и анализироват лямбда выражения? Тогда используйте Expression Trees. Для простых случаев будет хорошо, но если вы надумали написать свой ORM, то это занятие совсем не из простых. При этом помните, что для Expression Trees поддерживаются только однострочные лямбда-выражения. Если вам нужен динамически генерируемый HTML (вдруг), а это не веб-приложение, т используйте RazorEngine. Также симулируя веб-приложение, можно добавить поддержку Intellisense. Генерация кода Если вкратце: если вам нужна всё же полноценная генерация кода, то используйте Roslyn Потому что мощный и платформенно независимый. Возможно также будет полезен T4 (особенно если нужен не-C# и не-VB код). Преимущества и недостатки Reflection.Emit Преимущества создание динамической сборки на чистом IL: вы полностью контролируете код и может писать высоко оптимизированный код. Преимущество тут ещё в том, что если есть какая-то возможность недоступная в C#, но доступная в CLR, вы тоже сможете её задействовать. хорошая производительность генерации кода. Созданный вами код сразу компилируетс в сборку (в отличие от Microsoft.CodeDom, но c Roslyn всё хорошо, ибо он дёргает компилятор как сервис). платформенно-независимый подход (в отличие от Microsoft.CodeDom, который специфичен только для реализации Microsoft, но Roslyn тоже платформенно независим) Недостатки приходится прописывать простейшие вещи вроде циклов (просто серия переходов). Вам приходится думать на низком уровне. Поэтому сложно создавать нетривиальные программы. вы обязаны генерировать новую сборку. Если вам это подходит, то это не недостаток Но если вы хотите что-то добавить в существующую, то это невозможно средствами Reflection.Emit. Исходя из этого решайте применять вам Reflection.Emit или нет. Но я полагаю, чт это совсем хардкор. Альтернативы Microsoft.CodeDom Roslyn T4 Microsoft.CodeDom vs Reflection.Emit Способы программирования Сгенерированный код с помощью Microsoft.CodeDom может быть частью решения (класс LINQ to SQL, WSDL, XSD сгенерированы таким способом). Вы также можете использовать partial классы для настойки сгенерированного кода. Когда используете Reflection.Emit вы обязаны создавать новую сборку. Когда вы используете Reflecion.Emit вы программируете с помощью IL-команд. Когд вы программируете c помощью Microsoft.CodeDom вы создаёте команды C#. Поэтому код Microsoft.CodeDom будет проще создавать, так вы программируете боле высокоуровневыми командами. Код созданный на Reflecion.Emit будет сложней создать на IL (к примеру циклы надо будет создавать с помощью переходов), особенно когда вы программируете сложную логику. Из этого следует, что возможностей на Reflection.Emit больше (так у IL больше возможностей, чем у C#). Однако для каждой привычной вещи на C# придётся писать намного больше кода. Производительность Код Microsoft.CodeDom будет медленней, чем Reflection.Emit, так как он компилируется затем генерируется C#-код, затем этот сгенерированный код компилируеся. Reflection.Emit сразу компилируется в сборку. Платформенная (не)зависимость Как верно заметил @Grundy это платформенная независимость для Reflecion.Emit. А Microsoft.CodeDom доступен только на платформах от Microsoft. Roslyn (основано на вопросе) Однако я бы не использовал Microsoft.CodeDom. Ибо Roslyn мощней и выразительней. Roslyn есть на Mono (платформенно-независимый). Roslyn в отличии от Microsoft.CodeDom не просто является обёрткой на csc.exe, а може также анализировать код семантически, динамически компилировать и разбирать код. Это способ создавать любой инстумент для C# и VB. Поэтому есть возможности от форматирования код до нахождения всех ссылок в решении для данного символа. T4 А что если вам не надо динамически генерируемую сборку, а нужно дополнить часть существующего решения? Или если надо динамически сгенерированные файлы, но не C# или VB? К примеру, скаже JavaScript или CSS. (Для HTML рекомендую RazorEngine если что, но с помощью T4 тоже можно). Или вообще какого-то экзотического расширения. И ещё, усугубив задачу, этих файлов надо несколько, но количество известно только в момент исполнения кода. Тогда вы можете использовать T4. Это инструмент в Visual Studio (или просто консольная утилита), который используется для генерации кода любого языка. Также доступен и в MonoDevelop. Сразу скажу, что Entity Framework (не .NET Core) использует T4. Концептуально похоже на ASPX, только код между C#-вставками <# #> не обязательн HTML/CSS/JavaScript, а на языке программирования целевого файла. Мануалы. Из минусов Нет встроенной поддержки подсветки синтаксиса и Intellisense в Visual Studio, поэтому приходится либо привыкать, либо устанавливать третьесторонние плагины для Visual Studio.

Ответ 2



Как уже сказано в предыдущем ответе, существует несколько способов кодогенерации Часть из них позволяют генерировать код в дизайн-тайме, т.е. в IDE (T4). Другая - генерировать C# код во время исполнения (CodeDom), а третья - генерировать исполняемый код (т.е. IL) во время исполнения (Reflection.Emit). И хотя может показаться, что последний случай довольно редкий, но это не так. Даже не задумываясь, вы этим делом пользуетесь постоянно. Компилируемые регулярные выражения Да, если вы создаете регулярное выражение следующим образом: var regex = new Regex("abc" RegexOptions.Compiled);, то среда исполнения сгенерирует класс со специализированным конечным автоматом с помощью Reflection.Emit, а не будет интерпретировать регулярное выражение каждый раз. Сериализаторы Да, большинство сериализаторов используют отражение, но это весьма медленная штука Поэтому тот же XmlSerializer сгенерирует специальный класс сериализатора во время исполнени и сохранит его в специальной папке для последующего использования. Этот же подход используется и бинарным сериализатором, да и любимый всеми Newtonsoft.Json не брезгует кодогенерацией. PostSharp и прочие AOP Например, мы хотим сделать ран-тайм декоратор для логгирования. Если у нас есть виртуальны метод, помеченный атрибутом [LogMethod], то мы можем сгенерировать класс наследника, который переопределит этот виртуальный метод и будет логировать все аргументы метода, а уже потом вызывать базову реализацию. Постшарп и другие AOP фреймворки построены непосредственно на технологии генерации кода во время исполнения. Деревья выражений Да, Expression Trees в C# - это высокоуровневая конструкция, но метод Expression.Compile на самом деле использует Reflection.Emit для генерации кода во время исполнения. Object-to-object mappers Всякие Automapper-ы используют Reflection.Emit для генерации соответствия между отображением экземпляров одного типа в другой. ORM Это частный случай мапперов и в них используется кодогенерация по полной программе NHibernate, LINQ 2 SQL, Entity Fraework, все они генерируют классы во время исполнения для маппинга объектов друг на друга или для перехвата вызовов метода репозиториев. Mock frameworks Rhyno Mocks, Moq и другие, все они основаны либо на Expression Trees либо напряму на Reflection.Emit-е (да, многие mock framework-и используют DynamicProxy, который уже использует Reflection.Emit) для генерации заглушек при тестировании. WCF и прочие коммуникационные штуки Да, и в них тоже генерируются различные прокси классы для инспекции вызовов и другой инструментации во время исполнения. Application Insights и прочие логгеры/анализаторы И снова, для того, чтобы сделать процесс логгирования наиболее простым, в подобных инструментах используется кодогенерация. DLR - Dynamic Languagage Runtime В CLR есть специальная функциональность для взаимодействия с динамически типизированным языками, типа Python, Ruby etc. Там на полную катушку используется DLR для повышения производительности. Вы не поверите, но вы можете использовать Reflection.Emit (хоть и косвенно) даж для обхода стандартных проблем языка C#. Возможно вы знаете, что new T() ограничение для дженериков - это страх и ужас. Та используется отражение, что серьезно сказывается на производительности, да еще и заворачивае исключения, возникшие при создании экземпляра, в TargetInvocationException. Я уже в нескольких проектах сталкивался, что этот метод существенно портит end-to-end время исполнения. Поэтому в этих проектах используется специализированный CustomActivactor: public static class CustomActivator { public static T CreateInstance() where T : new() { return ActivatorImpl.Factory(); } private class ActivatorImpl where T : new() { // Под колпаком используем Reflection.Emit! private static readonly System.Linq.Expressions.Expression> _expr = () => new T(); public static readonly Func Factory = _expr.Compile(); } } Теперь наш активатор не страдает от проблем с производительностью и не оборачивает исключения из конструкторов в занудный TargetInvocationExpression. З.Ы. На самом деле, я коснулся лишь части сценариев, тот же ASP.NET использует кодогенераци по полной. Чтобы понять всю полноту картины можно попробовать поискать самому. Например, можно поискать тип ILGenerator или что-то еще из пространства имен System.Reflection.Emit: На гитхаб: найдено 40К ссылок на ILGenerator (там нет семантического поиска, поэтому многие ссылки будут не валидными, но дадут понять масштаб трагедии). в сорцах МС-а: десятки ссылок и примеров использования, большую часть которых я привел ранее. UPDATE: данная тема вылилась в довольно длинный пост, посвященный тонкостям работ активатора, обобщений и кодогенерации - Dissecting the new() constraint in C#: a perfect example of a leaky abstraction.

Git fatal: LF would be replaced by CRLF


Добавляю в репозиторий файл, git ругается следующим образом:


  fatal: LF would be replaced by CRLF in Web/Scripts/Bootstrap/bootstrap.js


говорю хорошо не проблема, делаю следующее


Открываю bootstrap.js в Notepad++;
Правка=>Формат Конца Строк=>Преобразовать в Win-формат (CRLF);
Сохраняю файл.


Делаю вновь git add *.js:


  fatal: LF would be replaced by CRLF in Web/Scripts/Bootstrap/bootstrap.js


git был настроен следующим образом:

git config --global core.autocrlf true
git config --global core.safecrlf true


Подскажите, как необходимо настроить git, чтобы избавиться от данной ошибки?
    


Ответы

Ответ 1



скорее всего, в результате выполнения этого действия: Правка=>Формат Конца Строк=>Преобразовать в Win-формат (CRLF); в файле всё-таки остался хотя бы один «одиночный» символ lf. как необходимо настроить git что бы избавиться от данной ошибки? совсем «избавиться» от сообщения можно, вернув значение по умолчанию: $ git config --global core.safecrlf false заменить же сообщение об ошибке на предупреждение можно так: $ git config --global core.safecrlf warn дополнительные сведения можно почерпнуть в man-странице git-config.

Ответ 2



Если вы работаете в jetBrains idea: 1) внизу справа, увидите такой значок Здесь вы можете поменять формат для текущего файла на crlf, а затем повторить git push Вот полезная ссылка по настройке проекта 2) Если хотите изменить формат всех файлов, то в верхнем меню идеи найдите: File > Line Separators > CRLF.

Ответ 3



git config core.autocrlf input

Как сделать на CSS колечки на линии


Кто нибудь с таки сталкивался? Нужно в landing page реализовать три колечка на линии. 
    


Ответы

Ответ 1



Пример * { padding: 0; margin: 0; box-sizing: border-box; } .circles { border-bottom: 20px solid #00878B; } .circle { position: relative; border: 20px solid #00878B; width: 150px; height: 150px; margin: 0 auto -20px auto; border-radius: 50%; box-shadow: 0 3px 5px rgba(0, 0, 0, .2); } .circle:after { content: ''; position: absolute; bottom: -20px; right: 0; width: 50px; height: 20px; background: #00878B; box-shadow: 0 -5px 5px -5px rgba(0, 0, 0, .5); }


Ответ 2



Вариант на градиентах. В этом случае нет тени на белом фоне. .circle { width: 200px; height: 200px; background-color: darkcyan; background-image: linear-gradient(90deg, darkcyan 50%, transparent 50%), linear-gradient(transparent 70%, rgba(0, 0, 0, .2) 85%, transparent 85%); background-repeat: norepeat-x; border-radius: 100%; position: relative; } .circle::after { content: ''; background-color: darkcyan; width: 50%; height: 15%; position: absolute; bottom: 0; left: 50%; } .circle::before { content: ''; background-color: darkcyan; background-image: linear-gradient(-90deg, rgba(0, 0, 0, .2) 30%, transparent 85%); width: 50%; height: 15%; position: absolute; bottom: 0; left: 0; z-index: -100; } .content { width: 70%; height: 70%; background-color: white; border-radius: 100%; position: absolute; left: 15%; top: 15%; }


Ответ 3



Основано на предыдущем ответе soledar10. Для автора. 3 колечки * { padding: 0; margin: 0; box-sizing: border-box; } .circles { float: left; width: 100%; border-bottom: 20px solid #00878B; } .circle { position: relative; border: 20px solid #00878B; width: 150px; height: 150px; margin: 0 auto -20px auto; border-radius: 50%; box-shadow: 0 3px 5px rgba(0, 0, 0, .2); float: left; } .circles>.circle:first-child{ margin-left: 50px; } .circles>.circle:nth-child(2){ margin-left: 40px; margin-right: 40px; } .circle:after { content: ''; position: absolute; bottom: -20px; right: 0; width: 50px; height: 20px; background: #00878B; box-shadow: 0 -5px 5px -5px rgba(0, 0, 0, .5); }


Inline инициализация полей


Рихтер в свой книге пишет, что если инициализировать поля inline, то в каждом конструктор
генерируется одинаковый IL-код инициализации этих полей и поэтому он советует не делать все inline, а выносить все в стандартный конструктор, а его вызывать из других конструкторов.

Актуально ли это сейчас? Почему microsoft сделали это именно так?
    


Ответы

Ответ 1



Для начала, техническая сторона вопроса. если инициализировать поля inline, то в каждом конструкторе генерируется одинаковый IL-код инициализации этих полей Да, это так (для неделегирующих конструкторов, как подсказывает @PetSerAl, то есть конструкторов, не указывающих this(...) вместо base(...)). Современная версия C# компилирует вот такой класс public class C { int X = 1; public C() { Console.WriteLine("C()"); } public C(int y) { Console.WriteLine("C(int)"); } } в такой IL: .class public auto ansi beforefieldinit C extends [mscorlib]System.Object { // Fields .field private int32 X // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Code size 24 (0x18) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: stfld int32 C::X IL_0007: ldarg.0 IL_0008: call instance void [mscorlib]System.Object::.ctor() IL_000d: ldstr "C()" IL_0012: call void [mscorlib]System.Console::WriteLine(string) IL_0017: ret } // end of method C::.ctor .method public hidebysig specialname rtspecialname instance void .ctor ( int32 y ) cil managed { // Method begins at RVA 0x2069 // Code size 24 (0x18) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: stfld int32 C::X IL_0007: ldarg.0 IL_0008: call instance void [mscorlib]System.Object::.ctor() IL_000d: ldstr "C(int)" IL_0012: call void [mscorlib]System.Console::WriteLine(string) IL_0017: ret } // end of method C::.ctor } // end of class C Мы видим последовательность команд IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: stfld int32 C::X которая инициализирует поле X, в обоих конструкторах. Почему бы нам не вынести это в отдельный приватный конструктор, и не вызывать ег самостоятельно из каждого публичного конструктора? (Приватный метод не подходит, т. к. он не может инициализировать readonly-поля.) Технически это можно, но это не одно и то же. Разница начинается там, где у нас есть базовый класс с нетривиальным конструктором Дело в том, что инициализаторы производного класса выполняются до выполнения базового конструктора. А вот сам конструктор производного класса выполняется после выполнения базового конструктора. Рассмотрим такой код: public class B { public B() { Console.WriteLine("B constructor"); } } public class C : B { public static int Get1() { Console.WriteLine("Getting 1"); return 1; } int X = Get1(); public C() { Console.WriteLine("C Constructor"); } } Конструктор C с точки зрения IL-кода таков: X = Get1(); B::ctor(); Console.WriteLine("C Constructor"); и выведет, соответственно, Getting 1 B constructor C Constructor Если вы поместите инициализацию X в конструктор C, или в другой, вспомогательны конструктор класса C, то он будет выполнен лишь после окончания конструктора класса B. То есть, смысл кода будет другим. Хуже того, такое преобразование не всегда возможно! Например, рассмотрим класс System.Exception. [Serializable] public class CustomException : Exception { readonly int ErrorCode; public CustomException(string message) : base(message) { } protected CustomException(SerializationInfo info, StreamingContext context) : base(info, context) { } } Вынести общую часть в «общий» конструктор невозможно, т. к. общий конструктор будет не в состоянии вызвать правильный базовый конструктор. Лазейкой может быть объявление конструкторов так, чтобы все они за исключением одног вызывали другие конструкторы того же класса, при этом инициализацию полей следует оставить там, где она есть. Например, если добавить конструктор public C(int x) : this() { Console.WriteLine("C(int) Constructor"); } при вызове его получим Getting 1 B constructor C Constructor C(int) Constructor В этом случае инициализация полей присутствует только в коде последнего конструктора Впрочем, у этого трюка те же недостатки: не всегда возможно из «универсального» конструктора вызвать нужный базовый конструктор! С технической стороной дела мы вроде бы разобрались. Теперь о реальном применении. Я бы лично не заморачивался, и писал не «как экономнее», а как понятнее. Выигры от объединения в общий метод трёх-четырёх инициализаторов на копейку, а код становитс более сложным, и к тому же приходится переписывать его без понятной для читателя необходимости. К тому же, вы можете считать, что компилятор самостоятельно применил к вашему коду оптимизацию, известную как method inlining :) Ещё один аргумент за inline-инициализацию полей: то, что inline-инициализация происходи до вызова конструктора родительского типа, уменьшает шансы на обращение к неинициализированному объекту. Пример (одолжен из соседнего вопроса): class Parent { public Parent() { DoSomething(); } protected virtual void DoSomething() {} } class Child1 : Parent { private string foo = "FOO"; protected override void DoSomething() => Console.WriteLine(foo.ToLower()); } class Child2 : Parent { private string foo; public Child2() { foo = "FOO"; } protected override void DoSomething() => Console.WriteLine(foo.ToLower()); } Почему именно инициализиаторы пробегают до вызова базового конструктора, расписан у Эрика Липперта: Why Do Initializers Run In The Opposite Order As Constructors? Part One, Part Two.

Ответ 2



Я очень уважаю Рихтера за глубину изложения и технические подробности, но есть оди момент в его книгах, который меня очень беспокоит. Это его советы по поводу того, что хорошо, а что плохо в вопросах стиля или проектирования. Вот этот совет из их числа. Актуально ли это сейчас? Есть ряд советов, особенно в вопросах эффективности, на которые очень сложно ответит правильно, ибо "правильность" сильно зависит от вашего приложения. Например, человек который работал над достаточно высоконагруженным приложением может дать такой совет "Никогда не используйте LINQ". Совет вполне разумен, если речь идет о критических участках высоконагруженного приложения, но очень плох в общем случае, ибо применим лишь для жалких полупроцента приложений. Совет из серии "выделяйте конструкторы и не используйте field-like инициализаторы" звучит еще смешнее, поскольку применим еще к меньшему числу use case-ов. Последнюю пару лет я работаю над высоконагруженным приложением, в котором действительн приходится с осторожностью относиться к использованию LINQ-а, но я ни разу не задумывался над тем, а не перенести ли мне код в общий конструктор. Этот совет так же не актуален сегодня, как он был не актуален 10 лет назад. Точнее он актуален десятку человек на планете, которые занимаются .net framework-ом, corefx-ом и, пожалуй, разработчикам реюзабельных компонентов для unity. Идея совета вполне валидна: дублирование IL-кода в каждом конструкторе ведет к увеличению размеров сборки (что увеличит время ее загрузки) и к увеличению длительности JIT-компиляции. Теоретически - проблема существует. Практически же, при принятии решения об использовани или не использовании field-like инициализации нужно отталкиваться от читабельности кода, а не от размера генерируемого IL-кода. Почему microsoft сделали это именно так? Сходу очень сложно придумать альтернативные простые и работающие подходы. Можн было бы выкусить всю инициализацию и поместить ее в закрытый метод, пометить его MethodImpl(AggressiveInline) аттрибутом, и дернуть его из каждого конструктора (*). Не исключаю, что авторы C#-а даже могли проверить это решение на практике и прийт к заключению, что разницы нет и оно того не стоит. Ну а если сейчас даже и будут подобны доказательства, то уже поздно и менять вряд ли кто-то что-то будет, ибо обратная совместимость. Да, компиляторостроители не горят желанием ломать фундаментальные вещи по генерации кода, поскольку в мире есть много тулов, которые инспектируют IL и будут очень удивлены, увидев там вызов левого метода. (*) Я тут не согласен с ув. @VladD по поводу того, что приватный метод здесь не подойдет Я не уверен, что CLR на самом деле энфорсит правило, что readonly поля должны инициализироваться только в конструкторе, поэтому сгенерированный компилятором закрытый метод вполне мог бы эти поля инициализировать.

Знак ! в арифметических выражениях, зачем он?


Смотрел я как то, не так давно, почти что только что, код своего преподавателя, набирался уму - разуму, да попивал чай зеленый с коньячком, и вдруг встречаю я странную штуку:
k=k+!(k%2);

Углубился я в раздумья - "встречал то раньше я знак восхищения только в логически
выражениях, а тут вот на тебе"... И как бы я часто клавишу F10 не нажимал так понят
и не смог, зачем оно и что делает. Помню, как то, человек хороший, сказал мне мудрость вот такую, что напиши ты int a=0; a следом сразу a=~a; то а в мгновенья станет -1, так вот, возможноль, что и этот случай, который был описан выше, причастен к фокусам из этих серий?     


Ответы

Ответ 1



Да, это из той же серии, что и ~a. На самом деле !a эквивалентно (a == 0) и имеет тип bool. Но когда вы интерпретирует это значение как int (а у вас так и происходит, из-за сложения с целым), bool превращаетс в 0 или 1. Таким образом, как целое число наше выражение эквивалентно a == 0 ? 1 : 0. В частности, если известно, что a -- 0 или 1, то это просто меняет значение на противоположное: если был ноль, станет 1, иначе станет 0. Таким образом, выражение k+!(k%2) следует понимать так: если k было чётным, k%2 == 0, ! обращает значение в 1, и в результате получается k+1. если k было нечётным, k%2 != 0, ! обращает значение в 0, и в результате получается просто k. То есть результат -- ближайшее (сверху) нечётное число. Makes sense? Такая техника, как в вашем примере -- один из примеров кода, за который одни обожают а другие ненавидят язык С++ (и С). Специалисты, тяготеющие к С, обычно больше любя такую лаконичную запись, а те, кто видит в языке скорее высокоуровневые конструкции, чем биты и байты, часто морщатся от такого кода. Я отношу себя к любителям высокоуровневых языков, но мне такие лаконичные конструкции нравятся. Вот тут собрано много примеров подобного кода. Почитайте! Ещё несколько мозгоразрывающих примеров на SO: здесь и здесь. (Вынес из комментариев) Кстати, обратите внимание, что более коротким (и возможно более эффективным) кодом, выполняющим ту же задачу, является k |= 1; Он, кстати, работает и для отрицательных значений k (это не вполне очевидно).

Ответ 2



Знак ! обозначает отрицание. !0 равен 1, !все_остальное равно 0. Смысл k=k+!(k%2); Если k четное, оно увеличивается на единицу. Более короткая запись k|=1; Если разложить по шагам: X=k%2 if(X) X=0; else X=1; k=k+X;

Ответ 3



Вот @Rules, возможно, пытался сказать что-то более-менее правильное. Но до конц он так вам все и не объяснил. Дело в том, что логический тип в С++ - это алиас целочисленног типа, но, в отличие от целых чисел типа int, например, тип bool имеет целочисленные значения в диапазоне от [0;1]. Короче говоря, логический тип может принимать только два значения - 0(false), 1(true). Сказанное мною вы можете сами проверить, приведя "логику" к числам так, например: int a=(int)true; // 1 int b=(int)false // 0 И приводить, кстати говоря, явным образом не обязательно. Обратный пример: bool a=48555; // 1 bool b=0; // 0 bool b=-948; // 1( из этого следует, что false`ом может быть только нуль ) Из всего этого следует, что эта "конструкция": k=k+!(k%2); будет либо инкрементировать K, либо оставлять неизменным. Если (k%2)>0, то прибавляется нуль ( т.к !(true) - это false ), иначе - единица( т.к !(false) - это true). И никаких величин более единицы, как это вам показал @Rules ! P.S И вам, и пользователю @Rules было бы неплохо ознакомиться с булевой алгеброй.

Ответ 4



k=k+!(k%2); Значит: k равно k плюс не(остаток от деления k на 2); не(1) = 0; не(0) = 1; 5%2 = остаток от деления 5 на 2 = 1 (т.е (4/2)+1); 23%7 = остаток от деления 23 на 7 = 2 (т.е (21/7)+2); 24%2 = остаток от деления 24 на 2 = 0 (т.е (24/2)+0); итак: если k = 4 то k = 4 + !0 => k = 4 + 1 => k = 5; если k = 17 то k = 17 + !1 => k = 17 + 0 => k = 17 то есть k увеличивается на единицу если оно изначально делилось на 2 PS: забыл упомянуть: !(любое число кроме 0) = 0

Зачем нужен паттерн “итератор”?


На курсах C# регулярно говорят о паттерне "итератор". Зачем он вообще нужен есл
мы можем нужные элементы отправить в коллекцию и работать уже с ней? Зачем нужны пользовательские коллекции если можно использовать стандартные?
    


Ответы

Ответ 1



Итератор это "не пользовательские коллекции", а инструмент обхода этой коллекци и не только коллекции, а вообще всего, что можно последовательно перебрать по какому то алгоритму. Обход коллекции всего лишь частный случай. Алгоритм перебора скрыт внутри итератора, что позволяет иметь отдельно сам объект данных и много итераторов с разными алгоритмами обхода (SRP) То есть итератор - класс инкапсулирующий в себе какой алгоритм обхода данных со стандартизированным интерфейсом "перечислитель"

Ответ 2



Итератор абстрагирует от вас не только саму коллекцию, но и состояние её обхода Например, для массива это текущий индекс, для структур, основанных на дереве, это текущи узел, и так далее. Без итераторов вам пришлось бы для каждой коллекции знать, как именно её обходить, и иметь доступ к её внутренним структурам (например, sorted set, основанный на дереве, обычно не выставляет свои узлы в публичный доступ). Зачем нужны пользовательские коллекции? Очень просто: стандартные не всегда умею то, что вам нужно. Например, если вы хотите смоделировать взаимно-однозначное соответствие (то есть типа Dictionary, но чтобы можно было также по значению быстро находить ключ), вам придётся писать свою коллекцию.

Где правильно инициализировать обьекты?


Меня учили, что все переменные и обьекты нужно инициализировать в onCreate() , ну в смысле глобальные (как правило).

Но уже несколько раз встречаю в коде опытных разработчиков когда например листы инициализируются прям там где обьявляются переменные

private OnVideoSelectedListener mCallback;


private AdapterList mAdapterList = null;

private ArrayList> mTempVideoData = new ArrayList<>();
private ArrayList> mVideoData     = new ArrayList<>();

private String mNextPageToken = "";
private String mVideoIds = "";
private String mDuration = "00:00";

private boolean mIsStillLoading = true;


Не сколько это правильно и ничему ли это не мешает? Например быстродействию программы или еще что то?  
    


Ответы

Ответ 1



Где правильно инициализировать обьекты? Согласно документации, к инициализации объектов есть только одно требование: Пол и переменные должны быть инициализированны до того как они будут использоваться. Так что ответ будет - в любом месте. Меня учили, что все переменные и обьекты нужно инициализировать в onCreate()... Я так понимаю что вопрос больше про андроид. OnCreate() - это метод который предназначе для инициализации Activity и он будет вызван как только вы запустите Activity с интентом Это нормально и общепринято, инициализировать поля в onCreate методе. Я думаю что главна идея сделать инициализацию Activity в onCreate это сократить время старта приложения. Когда ваше приложение запускается, андроид создает экземпляры всех классов (не уверен на счет всех) и если что-то долгое выполняется в конструкторе, то это замедлит старт приложения. Это также относится к выделению памяти для полей класса. Хотя конечно, это мизерное время. Но стоит помннить, что если вы попытаетесь использовать Activity как Activity activity = new Activity(); activity.someMethod().... тогда, onCreate() метод вызыван не будет. В общем, касательно вопроса где инициализировать, наиболее распростороненны два способа: - там где переменная объявлена - в конструкторе оба правильные, я предпочитаю констркторы. В любом случае старайтесь следовать одному стилю. Для полноты ответа отмечу, что вы также можете инициализировать поля в инициализационном блоке class A { private String myField; { myField = "..."; } }

Ответ 2



Можно прострелить ногу при наследовании. Псевдокод: class A { private List items; public void onCreate(){ items = new ArrayList<>(); } public void onStart(){ showList(); } public void showList(){ ((ListView)findViewById(R.id.listview)).setAdapter(new FooAdapter(items)); } public void setItems(List items){ this.items.clear(); this.items.addAll(items); } } class B extends A { @Override public void onCreate(){ showList(); } } class C extends A { @Override public void onCreate(){ showList(); super.onCreate(); } } class D extends A { @Override public void onCreate(){ setItems((List)getIntent().getSerializable("list")); super.onCreate(); } } Если инициализацию перенести в месте объявления (или в нестатическом блоке, или конструкторе), поведение программы станет более предсказуемым и вариантов сломать базовый класс будет меньше.

Ответ 3



Нашел интересную статью, в которой описано пять советов по оптимизации кода. Совет №1. Всегда, когда возможно, используйте локальные переменные вместо общедоступных полей класса Ограничивая область видимости переменных, вы не только улучшите читаемость кода уменьшите число потенциальных ошибок, но и сделаете его лучше подходящим для оптимизации. В блоке неоптимизированного кода, который показан ниже, значение переменной v вычисляетс во время исполнения приложения. Это происходит из-за того, что данная переменная доступн за пределами метода m() и может быть изменена в любом участке кода. Поэтому значение переменной неизвестно на этапе компиляции. Компилятор не знает, изменит ли вызов метода some_global_call() значение этой переменной, или нет, так как переменную v, повторимся, может изменить любой код за пределами метода. В оптимизированном варианте этого примера v – это локальная переменная. А значит её значение может быть вычислено на этапе компиляции. Как результат – компилятор может поместить значение переменной в код, который он генерирует, что поможет избежать вычисления значения переменной во время выполнения. Неоптимизированный код: class A { public int v = 0; public int m(){ v = 42; some_global_call(); return v*3; } } Оптимизированный код: class A { public int m(){ int v = 42; some_global_call(); return v*3; } } Совет №2. Используйте ключевое слово final для того, чтобы подсказать компилятору то, что значение поля – константа Ключевое слово final можно использовать для того, чтобы защитить код от случайног изменения переменных, которые должны быть константами. Однако оно позволяет улучшить производительность, так как подсказывает компилятору, что перед ним именно константа. Во фрагменте неоптимизированного кода значение v*v*v должно вычисляться во врем выполнения программы, так как значение v может измениться. В оптимизированном вариант использование ключевого слова final при объявлении переменной и присвоении ей значения, говорит компилятору о том, что значение переменной меняться не будет. Таким образом, вычисление значения можно произвести на этапе компиляции и в выходной код будет добавлено значение, а не команды для его вычисления во время выполнения программы. Неоптимизированный код: class A { int v = 42; public int m(){ return v*v*v; } } Оптимизированный код: class A { final int v = 42; public int m(){ return v*v*v; } } Совет №3. Используйте ключевое слово final при объявлении классов и методов Так как любой метод в Java может оказаться полиморфными, объявление метода или класс с ключевым словом final указывает компилятору на то, что метод не переопределён ни в одном из подклассов. В неоптимизированном варианте кода перед вызовом функции m() нужно произвести е разрешение. В оптимизированном коде, из-за использования при объявлении метода m() ключевог слова final, компилятор знает, какая именно версия метода будет вызвана. Поэтому он может избежать поиска метода и заменить вызов метода m() его содержимым, встроив его в необходимое место программы. В результате получаем увеличение производительности. Неоптимизированный код: class A { public int m(){ return 42; } public int f(){ int sum = 0; for (int i = 0; i < 1000; i++) sum += m(); // m must be resolved before making a call return sum; } } Оптимизированный код: class A { public final int m(){ return 42; } public int f(){ int sum = 0; for (int i = 0; i < 1000; i++) sum += m(); return sum; } } Совет №4. Избегайте вызывать маленькие методы через JNI Существуют веские причины использования JNI-вызовов. Например, если у вас есть ко или библиотеки на C/C++, которые вы хотите повторно использовать в Java-приложениях Возможно, вы создаёте кросс-платформенное приложение, или ваша цель – увеличение производительност за счет использования низкоуровневых механизмов. Однако, важно свести количество JNI-вызово к минимуму, так как каждый из них создаёт значительную нагрузку на систему. Когда JNI используют для оптимизации производительности, эта дополнительная нагрузка может свести на нет ожидаемую выгоду. В частности, частые вызовы коротких, не производящих значительной вычислительной работы JNI-методов, способны производительность ухудшить. А если такие вызовы поместить в цикл, то ненужная нагрузка на систему лишь увеличится. Пример кода: class A { public final int factorial(int x){ int f = 1; for (int i =2; i <= x; i++) f *= i; return f; } public int compute (){ int sum = 0; for (int i = 0; i < 1000; i++) sum += factorial(i % 5); // if we used the JNI version of factorial() here // it would be noticeably slower, because it is in a loop // and the loop amplifies the overhead of the JNI call return sum; } } Совет №5. Используйте стандартные библиотеки вместо реализации той же функциональности в собственном коде Стандартные библиотеки Java серьёзно оптимизированы. Если использовать везде, гд это возможно, внутренние механизмы Java, это позволит достичь наилучшей производительности Стандартные решения могут работать значительно быстрее, чем «самописные» реализации. Попытка избежать дополнительной нагрузки на систему за счёт отказа от вызова стандартной библиотечной функции может, на самом деле, ухудшить производительность. В неоптимизированном варианте кода показана попытка избежать вызова стандартной функци Math.abs() за счёт собственной реализации алгоритма получения абсолютного значения числа. Однако, код, в котором вызывается библиотечная функция, работает быстрее за счёт того, что вызов заменяется оптимизированной внутренней реализацией в ART во время компиляции. Неоптимизированный код: class A { public static final int abs(int a){ int b; if (a < 0) b = a; else b = -a; return b; } } Оптимизированный код: class A { public static final int abs (int a){ return Math.abs(a); } } P.S. Надеюсь хоть чем-то да помог.

Ответ 4



Ваш вопрос довольно абстрактный. По существу могу отметить что критически это н на что не влияет, коллеции инициализируют сразу чтобы потом не забыть, так как даж методы неинициализированой коллекции компилятор пропустит, а потом будут вылетать NPE Фактически инициализация этих переменных произойдет до начала работы метода onCreate и увеличит время между вызовом активити(например из другого активити) и началом работы метода onCreate. Но эта разница ничтожно мала. Чтобы ощутить разницу, вам нужно инициализировать сотни тысяч глобальных переменных. Так что если не придираться к словам, это ничему не мешает. В он onCreate же делается инициализация потому что там вы уже можете получить доступ к различным данным(например которые были переданы через Extra и т.д...)

Агрегация и композиция


Здравствуйте!

Всегда считал, что агрегация — это синоним композиции, однако наткнулся на блог в интернете, где приводятся отличия композиции от агрегации.

Мне это снесло крышу. Поясните, пожалуйста, плюсы/минусы и того и другого на небольших примерах. Как это влияет на расширяемость, тестируемость и т. д.
    


Ответы

Ответ 1



Существует несколько видов взаимодействия объектов, объединенных под общим понятие "Has-A Relationship" или "Part Of Relationship". Это отношение означает, что один объект является составной частью другого объекта. Существует два подвида этого отношения: если один объект создает другой объект время жизни "части" зависит от времени жизни целого, то это называется "композиция", если же один объект получает ссылку (указатель) на другой объект в процессе конструирования, то это уже агрегация. Давайте рассмотрим пример из .NET Framework, чтобы увидеть, какие ограничения/последствия несут эти отношения: StringWriter + StringBuilder. Класс StringWriter - это специализированная версия класса TextWriter, которые активно используются при сериализации и для получения текстового представления объектов. Конкретно StringWriter создает строковое представление объекта или графа объекто и опирается на экземпляр StringBuilder в своей работе. Т.е. мы можем сказать, что 'StringWrite HAS A StringBuilder' или 'StringBuilder is part of StringWriter'. Теперь давайте посмотрим, как решить, должен ли StringWriter получать экземпляр StringBuilder-а извне или создавать его? С одной стороны, нам, как клиенту класса StringWriter часто бывает все равно, чт именно используется внутри этого класса для получения строкового представления. Это значит, что с точки зрения простоты использования лучше, чтобы StringWriter создавал экземпляр StringBuilder-а самостоятельно. Но, с другой стороны, конкретный объект StringWriter-а может отвечать лишь за получени части строкового представления, а другая часть строки может вычисляться другим способом С этой точки зрения, лучше, чтобы StringWriter принимал экземлпяр StringBuilder-а в конструкторе. Это же справедливо и для высоконагруженных систем, в которых разумно использование пула объектов. Поскольку StringWriter - это библиотечный класс, который должен поддерживать об сценария, то у него есть перегруженные версии конструктора: один из них создает StringBuilder внутри, а другой - принимает его снаружи. Другими словами, выбор между композицией и агрегацией основан на соблюдении балланса между различными требованиями в дизайне: Композиция: объект A управляет временем жизни объекта B Плюсы: Композиция позволяет скрыть отношение использования объектов от глаз клиента. Делает API использования класса более простым и позволяет перейти от использовани одного класса, к другому (например, StringWriter мог бы поменять реализацию и начать использовать другой тип, например CustomStringBuilder). Минусы: Отношение достаточно жесткое, поскольку один объект должен уметь создавать другой он должен знать конкретный тип и иметь доступ к функции создания. Композиция не позволяе использовать интерфейсы (без привлечения фабрик) и требует, чтобы класс имел доступ к конструктору другого класса: представьте, что конструктор StringBuilder-а является внутренним или же это интерфейс IStringBuilder и только клиенский код знает, какой экземпляр должен использоваться здесь и сейчас. Агрегация: объект А получает ссылку на объект B Плюсы: Более слабая связанность между объектом и его клиентом. Теперь мы можем использовать интерфейсы и одному объекту не нужно знать, как именно создавать другой объект. Большая гибкость. Вытекает из первого пункта Минусы: Выставление наружу деталей реализации. Поскольку клиент класса должен предоставит зависимость в момент создания объекта (передать экземпляр StringBuilder-а в момент создания StringWriter-а, то сам факт этого отношения становится известен клиенту. Из первого пункта вытекает увеличение сложности в работе клиентов, а также больша "жесткость" решения в долгосрочной перспективе. Теперь автор класса TextWriter уже н может принять решение самостоятельно и перейти от StringBuilder-а к чему-то другому. Да, можно "добавить еще один уровень абстракции" и выделить интерфейс IStringBuilder, но разорвать это отношение совсем будет невозможно без поломки всех существующих клиентов. В заключении: дизайн - это поиск компромисса между различными факторами. Композици проще с точки зрения клиентов класса, но налагает определенные ограничения: "целое должно уметь создавать "составную часть". Агрегация гибче, но налагает другие ограничения: теперь "целое" не скрывает о существовании "составной части", а значит и не сможет заменить ее на другую "составную часть" в будущем. P.S. Если очень хочется использовать пример из реального мира, то для объяснени композиции и агрегации может подойти ... отвертка. Если отвертка цельная, т.е. ручк и насадка намертво связаны друг с другом, то мы имеем отношение композиции. Если же насадка съемная и может существовать без ручки или же использоваться с другой ручкой, то мы имеем отношение агрегации.

Как хранятся в памяти литералы?


Как хранятся в памяти литералы?

int main() {
  5;
  1.;
  "";
}

    


Ответы

Ответ 1



С абстрактной точки зрения "в памяти" в языке С++ хранятся только lvalues. Из приведенных вами литералов только строковый литерал "" является lvalue, т.е. только он хранится в памяти. Остальные литералы не являются lvalue и формально положения в памяти не имеют. Фактически же целочисленные литералы обычно "встраиваются" в тот контекст, в которо они использованы. Они могут встроиться непосредственно в сгенерированную машинную команд (то есть стать частью кода, а не данных программы), а могут вообще неузнаваемо трансформироваться или бесследно раствориться в этом контексте: умножение на 8 может замениться на сдвиг влево на 3 разряда, а присваивание переменной значения 0 может замениться на машинную инструкцию обнуления. Что произойдет с литералом с плавающей точкой — зависит от способностей компилятор и особенностей подлежащей машинной платформы. На платформе x86 в общем случае плавающи литерал действительно будет храниться в памяти, т.е. для его хранения будет заведена внутренняя переменная. В то же время компилятор вправе распознавать "особенные" плавающие константы (0.0, 1.0 и т.д.) и реализовывать их неявным образом. Но, еще раз, в любом случае на уровне языка плавающий литерал положения в памяти не имеет.

Ответ 2



Арифметические литералы не хранятся в памяти, а встраиваются в объектный код. Строковые литералы хранятся обычно в отдельном пуле литералов, так как они имею статическую длительность используемой памяти. Одинаковые строковые литералы могут храниться как один литерал, либо как отдельные литералы в зависимости от опций компилятора. Поэтому данное выражение в if предложении if ( "A" == "A" ) { /* ... */ } может иметь значение как true, если эти два литерала хранятся как один литерал, т есть имеют один и тот же адрес, так и false, если компилятор хранит каждый из этих литералов в отдельном участке памяти. Что касается этой программы int main() { 5; 1.; ""; } то так как данные предложения выражений не имеют побочных эффектов, то для них может вообще не генерироваться никакой объектный код.

Ответ 3



Да никак... Пока они не использованы - для инициализации переменной, например, ил там в вызове функции. И то они могут не храниться - например, какое-нибудь a >>= 2 просто превратится в машинную команду, в которой просто указано непосредственное значение 2, без всякого хранения.

Программа определяющая засыпание пользователя [закрыт]


Есть идеи как реализовать? Что проверять, например? Можно что-нибудь со звуком, вебкамерой и т. д?
    


Ответы

Ответ 1



Звук и вебка ни к чему, они ничего не дают. Можно реализовать использование мыши например, используя функцию GetCursorPos и сравнивая значения. Если они различны - пользователь бодр и полон сил, иначе -- выполняем что-то свое (код, надеюсь, напишете сами). Намек на вариант с клавиатурой читайте здесь. Ну, и ещё один вариант, в отличие от остальных, не фоновый -- вывод сообщения с просьбой нажать кнопку ОК. UPD Насчет заснет в постели -- то ему проще использовать радиомышь, т.к. он спит в темноте где вебка ничего не различит. Ну, можете ещё свое устройство придумать, например, на основе маломощного лазера и фоторезистора... UPD2 Да, ещё вариант с концом фильма. Т.е. если фильм не идет, скажем, минуты три, и никакого действия со стороны юзера -- хорошо, делаем свою команду, скажем, выключаем комп...

Ответ 2



Как вариант - написать программу, которая через промежуток времени будет просит пользователя произнести какой либо звук (слово, фразу). Если через микрофон ничего не сказали, значит юзер спит. Но так придется использовать какой-то движок распознавания речи, ну, или на крайняк - анализировать уровень звука в микрофоне.

Ответ 3



Мышь не двигается долго, нет набора с клавиатуры, нет запущенных полноэкранных приложений.

Ответ 4



Ну, если принять во внимание, что во сне человек почти не двигается (в идеале), т можно обрабатывать видеоряд с вебкамеры и отлавливать момент, когда движения нет какой-то период времени. Еще можно отслеживать нажатия клавиш на клавитуре и движение мышкой.

Ответ 5



Можно останавливать фильм через какой то промежуток времени после последнего нажати клавиатуры/движения мышки. К примеру если 30 минут ничего не происходило, то делае паузу. Если человек спит то ему это не помешает, но если человек бодр, и смотрит фильм, то это несколько усложнит процесс просмотра:) В итоге человек либо заснет, либо встанет и нажмет на "плей"

Ответ 6



Разработай детектор храпа!!! Ну если серьезно - каждый человек индивидуален, те более, люди имеют свойство жить в семье, держать домашних животных. То есть, провед социологическое иследование - какие есть факторы "засыпания". Например, следующий профиль пользователя: все работает - через час уменьшаем звук на 25% (если не было событий от мыши или клавы) и далее... Только :)индикаторы будут мигать!

Ответ 7



Многие проигрыватели видео имеют такую функцию. Например, в моем любимом Media Playe Classic: Воспроизведение -> По окончании воспроизведения -> Выключить компьютер. Если же мы не заснули, то успеем отменить выключение, ведь в фильмах ещё очень долго идут титры.

Ответ 8



Если не ограничиваться аппаратурой компьютера (web-камера, микрофон и т.д.), то можн предложить использовать фитнес-трекер/умные часы (или как это сейчас называется). В них есть возможность измерять пульс, а так же передавать информацию на смартфон по bluetooth. P.S. Уже есть вот такое устройство)

Ответ 9



В принципе, я бы так делал: Один таймер проверяет координаты мыши, и если совпадают - запускал другой таймер, который по истечении определенного времени "что-то" делал. Надеюсь, что помог.

Почему интерфейсы упрощают жизнь?


Ранее никогда не писал собственные интерфейсы для "упрощения жизни". Почему? Чт
в них такого интересного? 

В комментариях попросили добавить в вопрос мои знания и область работы. Пишу androi
приложения и... Все. Об интерфейсах не знаю ничего, только то, что можно класс от интерфейса наследовать и переопределить методы. Но зачем? Смысл интерфейсов?

Спасибо за замечательные ответы!
    


Ответы

Ответ 1



Лично для меня самым наглядным для понимания сущности интерфейсов являются коллекци и все, что с ними связанно. Скорее всего это так, потому что с ними приходилось чаще всего работать. Одна из главных особенностей ООП - избавление от дублирования логики, за счет правильно архитектурной составляющей. Для данного случая (в общем понимании) и были придуманы интерфейсы и абстрактные классы. Допустим у меня есть сферический в вакууме метод, делающий какую-то работу с коллекцией: /*Здесь может быть любой тип коллекции, который реализует соотв. интерфейс. Причем я использую интерфейс самого "низкого" уровня, т.к. он полностью покрывает данный функционал (т.е. проход по элементам в цикле) Также я могу написать любую свою реализацию для этого интерфейса */ public void showCollectionAtConsole(Iterable col){ col.forEach(object -> System.out.println(object)); } Удобство здесь в том, что только в самой Java довольно много реализаций данного интерфейса, (подробнее) и я могу использовать данную логику для каждой из них. Теперь у меня есть другой метод, реализующий какой-то функционал: public boolean removeOrAddMyObject (Collection col, MyObject object){ //Если удалил объект - true, иначе - false boolean isRemove = true; if (col!=null){ if (col.isEmpty() || !col.remove(object)) { col.add(object); isRemove = false; } }else throw new IllegalStateException("Коллекция = null"); return isRemove; } Здесь мне приходится использовать три метода коллекции: isEmpty(), add(), remove() Однако все эти методы описаны в интерфейсе Collection и нет нужды в качестве аргумента передавать более "высокий" интерфейс или класс с реализацией. Как я уже писал выше, данное решение позволит мне с чистой совестью переиспользовать неоднократно данный метод для других реализаций. Также, как мне кажется, отличным примером использования интерфейсов являются Callback'и: public final class MyClass { static class MyObject { int id; String name; public MyObject(int id, String name) { this.id = id; this.name = name; } public void setId(int id) { this.id = id; } public void setName(String name){ this.name = name; } @Override public String toString(){ return "My Object: id = "+id+", name = "+name; } } //Интерфейс коллбэка interface MyCallback { void doSome(T myObject); } static void doSomeAwesome(MyObject object, MyCallback callback) { System.out.println(object); callback.doSome(object); System.out.println(object); } public static void main(String[] args) { MyObject myObject = new MyObject(1,"someName"); MyCallback callback = new MyCallback() { @Override public void doSome(MyObject myObject) { myObject.setId(2); myObject.setName("someOtherName"); } }; doSomeAwesome(myObject,callback); } } Суть сводится к тому, что в интерфейсе коллбэка вы явным образом определяете дженери (если хотите передавать в метод параметры и/или получать из метода объекты) и описывает контракт. В моем случае это один метод, принимающий на вход аргумент. Теперь, я могу реализовать метод коллбэка на свое усмотрение, и таким образом расширить использование метода doSomeAwesome() дополнительной логикой. Если выполнить код на выходе будет: My Object: id = 1, name = someName My Object: id = 2, name = someOtherName UPD: Также следует понимать, что есть и другие контексты для использования интерфейсов: контекст множественного наследования (в Java нельзя наследоваться от более 1 класса, но можно имплементировать более 1 интерфейса) интерфейс как прослойка между взаимодействующими сущностями. Т.е. есть два модул в программе которые выполняют разные функции и при этом взаимодействуют друг с другом В идеальной ситуации оба модуля должны представлять друг для друга "черный ящик" с необходимым минимумом методов, описываемых интерфейсами. Таким образом куда проще избежать сложностей при последующей отладке кода. контекст проектирования архитектуры приложения. Как правило, на начальном этапе проектировани архитектуры будущего приложения, очень удобно "прикидывать" взаимодействие между сущностями с помощью интерфейсов. Это как будто черновой вариант, который не требует "заморачиваться" с реализацией, и позволяет относительно безболезненно модифицировать архитектуру на ранних этапах. контекст тестирования. Как уже упоминали в одном из ответов, использование интерфейсо позволяет: "использовать Mock-объекты вместо настоящих". Поверьте, в перспективе это очень облегчает жизнь.

Ответ 2



Интерфейс предоставляет контракт, который должны выполнять все классы, реализующи этот интерфейс, и является абстракцией, показывающей что объект может делать, но как он этот делает - не важно. На практике это приводит к следующему: При использовании интерфейсов появляется возможность заменять один класс, реализующи интерфейс, на другой класс, реализующий этот же интерфейс, без переписывания всего кода. Например, если методу передаётся Map: public void handle(Map map) { ... } то не придётся менять описание метода, если потребуется использовать TreeMap вместо HashMap. Аналогично если метод возвращает Map: он может начать возвращать TreeMap вместо HashMap и трагедии библейских масштабов при этом не случится, так как код, работающий с возвращенным этим методом значением, имеет дело с Map. Это увеличивает гибкость кода: проще переключаться с одного источника данных на другой и с одной реализации на другую. Также это полезно при тестировании, так как позволяет использовать Mock-объекты вместо настоящих. Если нужно одинаково обрабатывать коллекции элементов (например, ArrayList и Set, возвращаемый методом keySet() у HashMap), то достаточно описать метод так: public void handle(Collection elements) { ... } Использование generics-типа здесь для большей реалистичности. Без использования интерфейса пришлось создавать два метода: public void handle(ArrayList elements) { ... } public void handle(Set elements) { ... } И либо дублировать код, либо, например, во втором методе создавать ArrayList, добавлять в него все элементы из Set и вызывать первый метод. Также использование интерфейсов позволяет объединить разные объекты, реализующи один и тот же интерфейс, в один список и одинаково их обрабатывать: public interface Animal { public void feed(); } public class Dog implements Animal { public void feed() { ... } } public class Cat implements Animal { public void feed() { ... } } List animals = new ArrayList<>(); animals.add(new Cat()); animals.add(new Dog()); for (Animal animal : animals) { animal.feed(); } Без использования интерфейса пришлось бы городить "if-else" (или "switch-case") с отдельной реализацией логики для каждого типа животных.

Ответ 3



Потому что интерфейс - это абстракция, которая говорит о том, что делать, а не ка делать. "упрощение жизни" заключается в том, что интерфейс задает единообразное ожидаемое поведение, и где бы вы его не встретили (если конечно программист не наплевал на контракт интерфейса). Вообще, чтобы понять, что я тут написал, вам бы почитать книги по ООП.

Ответ 4



Интерфейсы и в правду упрощают жизнь .Допустим в android для навигации gps Googl api использую интерфейс, для слушателя используют интерфейс (Интерфейсы используютс почти везде даже в потоках )А начиная с Java-8 интерфейсы реализуют и тело, так чт можно уже писать не только условие но и тело интерфейса .Но дело каждого использовать их или нет .Не всегда программист обязан реализовывать свои интерфейсы.Правила "написание хорошего кода" гласит:сперва составь интерфейсы абстракцию потом реализуй))

Requirements.txt - как получить?


Как получить requirements.txt, имея django проект?    


Ответы

Ответ 1



С помощью команды: pip freeze

Ответ 2



Чтобы получить список пакетов в проекте выполняем команду pip freeze Для записи вывода в requirements.txt дополняем следующим образом: pip freeze > requirements.txt Команду выполняем в корне проекта. Там же появится и файл. Подразумевается использовани активного venv для текущего проекта. При выполнении вне виртуальной среды текущего проекта могут быть подтянуты лишние пакеты, не связанные с текущим проектом!

Ответ 3



Лучше всего смотреть в INSTALLED_APPS и выковыривать всё вручную. С pip freeze ест вариант получить тыкву через некоторое время, ибо он вытягивает абсолютно все пакет вместе с их зависимостями (включая системные), а пакеты со временем обновляются, старые версии удаляются. Да и нечего вам рулить зависимостями вручную - это задача менеджера пакетов (pip или easy_install). Лучше всего заполнять файл requirements по мере написания проекта, а не после. Добавлено: Есть подходящая опция у pip: pip freeze --local. Из справки: If in a virtualenv that has global access, do not output globally-installed packages.

Ответ 4



Чтобы создать requirements.txt или обновить уже существующий файл, чтобы он соответствовал текущему virtualenv, можно использовать pip-dump команду: $ pip install pip-tools $ pip-dump Пакет работает на *nix системах.

Ответ 5



Для работы с requirements.txt есть Python библиотека pipreqs Установка: pip install pipreqs Чтобы сгенерировать файл requirements.txt используйте pipreqs /path/to/project/folder Если requirements.txt уже есть и необходимо его перезаписать, то используйте флаг —force Если нужно просто посмотреть на используемые библиотеки без создания, флаг —print Подробнее здесь

В каких случаях возникает потребность использования raw pointers в современном C++?


После появления новых возможностей в C++ таких как std::make_shared() и std::make_uniqu
то возникает вопрос : А есть ли хоть один случай, когда действительно нужно работать с сырым указателем?

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

Я беру среднего по уровню программиста на C++. Он пишет свою библиотеку без использовани
сторонего кода. И у меня не находится ни одного примера, когда ему действительно нужно пользоваться сырыми указателями.
    


Ответы

Ответ 1



Во внутренних деталях реализации, когда владеющие указатели усложняют взаимодействие когда нужно разделять владение, но время жизни объекта чётко детерминировано и не возникае вопроса: где и когда чистить объект (shared_ptr в таких случаях - лишнее), когда ссылкой не обойтись, потому как возможна ситуация с nullptr у объекта. В публичных API стараться избегать, пока это возможно, либо чётко обосновывать - "почему?" и "зачем?". В общем, я придерживаюсь правила: обходиться без сырых указателей, пока это возможно. Пока не подводило (embedded). Кстати, возможно забавно, но в C проектах, при достижении определённого уровня сложности тоже начинают появляются механизмы разделяемого владения. Это всем известные xxx_ref(object)/xxx_unref(object) Примеры: GLib со своим gobject, FFmpeg активно внедряет подсчёт ссылок для AVPacket и AVFrame (для таких случаев в Boost существует intrusive_ptr), ядро Linux тоже не стоит в стороне (https://lwn.net/Articles/336224/ по слову Reference Counting или http://www.makelinux.net/books/lkd2/ch17lev1sec7) со своим kobject.

Ответ 2



Самый банальный пример — дерево. Каждый узел хранит родителя. Можно, конечно, хранит в weak_ptr, но это не удобно и, на мой взгляд, глупо. Поэтому в этом случае используетс «голый» указатель. Можно придумать и другие примеры, но суть их будет следующая: «голые» указатели, в современном коде, можно и нужно использовать тогда, когда мы не владеем тем, на что ссылаемся и у нас есть уверенность, что то, на что мы ссылаемся не «умрёт» раньше, чем мы. На эту тему есть отличное видео от Страуструпа: Writing Good C++14. Львиная его доля посвящена именно указателям.

Ответ 3



Упомянутые "умные" указатели являются владеющими. Т.е. при вызове деструктора умног указателя данные либо освобождаются сразу (std::unique_ptr), либо уменьшается счетчик владения (std::shared_ptr), и при достижении нуля осуществляется освобождение данных. Если же указатель не владеет данными как раз имеет смысл использовать сырые указатели Небольшое исключение это std::weak_ptr - невладеющий указатель, но полученный из std::shared_ptr. Чаще всего наверное использовать сырые указатели приходится при взаимодействии со старым (до C++11) кодом или вовсе Си-кодом. Например, работа со строками char*.

Ответ 4



Хотите простейший пример? :) int main(int , char**) Обойтись можно без многого, просто бывают ситуации, когда этот обход куда дольше, чем прямая дорога.

Ответ 5



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

Зачем нужен тип long когда есть int?


В С есть тип long. Согласно: Wiki c-types он занимет в памяти 4 байта, да и диапазон значений у него такой же как и у int.
Тогда зачем он нужен?

Выписка из wiki: 

int:


  Основной тип целого числа со знаком. Может содержать числа в диапазоне
  [−32767, +32767]. range;[3][4] Таким образом, это по крайней мере 16
  бит (2 байта). На практике, во всех современных компиляторах имеет
  размер в 4 байта и диапазон [-2147483648, +2147483647]


long:


  Тип длинного целого числа со знаком. Может содержать числа, как
  минимум, в диапазоне [−2 147 483 647, +2 147 483 647].[3][4] Таким
  образом, это по крайней мере 32 бита (4 байта).

    


Ответы

Ответ 1



Процессоры развивались постепенно все более расширяя емкость регистров. Например ранее в DOS тип int соответствовал 16 разрядному целочисленному значению, так как н IBM-совместимых компьютеров регистры были 16-разрядными. Например, регистр AX является 16 разрядным, а его подрегистры AL и AH имели разрядность равную 8 битам. До этого вообще регистры процессоров были 8 разрядными. Если выполнить, допустим, умножение двух объектов 16-разрядного типа int, то дл хранения результата нужно использовать два регистра как, например, регистры AX и DX. Для этого результата уже нужно вводить новый целочисленный тип. И такой тип был введен. Это тип long . Затем появились 64-разрядные процессоры. Необходимо различать 16-разрядные целы числа, 32-разрядные целые числа и 64- разрядные целые числа. Поэтому был введен дополнительный целый тип Long long. Имейте в виду, что нужно было сохранять обратную совместимость с целыми типами, введенными ранее для процессоров с меньшей разрядностью. Поэтому в стандарте C принято, что sizeof( short ) <= sizeof( int ) sizeof( int ) <= sizeof( long ) sizeof( long ) <= sizeof( long long ) У разработчиков компиляторов есть свобода выбора, какая разрядность будет у тип long и других целочисленных типов. Например, разрядность типа long может составлять не `32 бита, а 64 бита. Чтобы программы были переносимы, возникла необходимость вводить новые типы со строго указанной разрядностью такие, как, например, int_least32_t или int32_t. На одних 64-битовых платформах тип long и тип long long могут иметь 64-битовуж разрядность На других 64-битовых платформах тип long может быть 32-битовым и тип int также может быть 32-битовым, а тип long long - 64 битовым. Тем не менее ранг типа long long больше ранга типа long, а тип long в свою очередь имеет ранг выше, чем тип int. На этом основываются правила преобразования типов в выражениях. Например, если тип long и тип int имеют разрядность равную 32 битам, то в следующем фрагменте кода long x = 0; unsigned int y = 0; x + y; тип выражения x + y имеет тип unsigned long. В связи с этим имеются некоторые курьезы, связанные с такими преобразованиями особенно при выборе перегруженной функции в C++. Один из таких примеров описан в следующей теме, где ближе к ее концу (можно быстр найти это сообщение по ключевому слову int64_t) описывается один из таких курьезов связанных с вызовом перегруженной функции, у которой параметр имеет тип std::int64_t, который, как оказалось, является не алиасом типа long long int, а алиасом типа long int, который на данной платформе является 64-битовым.

Ответ 2



Размеры типов int и long не регламентированы Стандартом языка. Но регламентировано отношение их размеров, т.е. sizeof(int) <= sizeof(long). На текущий момент можно сказать, что размер long зависит как от разрядности процессора так и от используемой ОС. Например, для Linux и MacOS он будет 8 байт для 64bit архитектур, и 4 байта для 32bit архитектур. В Windows размер будет 4 байта, независимо от разрядности архитектуры. Например, в Стандарте С++ даже встречается такой пример: static_assert(sizeof(long) >= 8, "64-bit code generation required for this library."); Использовать разные типы можно, например, для обеспечения перегрузки функций: void f(int) {} void f(long) {} f(42); // int f(42L); // long Также не стоит забывать, что данные типы имеют разный ранг (integer conversion rank), что влияет на правила преобразования целочисленных типов. Уточнение по поводу размеров long после комментариев @AnT со страницы cygwin: While the Mingw and Microsoft compilers use the LLP64 data model, Cygwin compiler use the LP64 data model, just like Linux. This affects the size of the type long. In the LLP64 model preferred by Microsoft, sizeof(long) is 4. This applies for the related Win32 types like LONG, ULONG, DWORD, etc., too. In the LP64 model used by Cygwin, sizeof(long) is 8, just like the size of pointer or the types size_t/ssize_t. This simplifies porting Linux applications to 64 bit Cygwin, but it requires due diligence when calling Windows functions taking LONG, ULONG, DWORD, or any other equivalent type. This is especially important in conjunction with pointers.

Ответ 3



Тип long не "занимает в памяти 4 байта". Он занимает как минимум 32 бита, т.е. имеет диапазон как минимум [−2147483647, +2147483647]. А сверху его размер не ограничен. Тип int занимает как минимум 16 бит, ибо имеет диапазон как минимум [−32767, +32767] (как вы сами заметили). Поэтому не совсем ясно, откуда вы взяли предположение об одинаковости диапазоно этих типов - уже из процитированного вами видно, что они запросто могут быть разными. В языке С++ система целочисленных типов обладает тем свойством, что sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long) т.е. вполне возможно что размеры (и диапазоны) этих типов все будут различными. В языке С, по каким-то историческим причинам, это соотношение определяется как диапазон(signed char) ⊆ диапазон(short) ⊆ диапазон(int) ⊆ диапазон(long) ⊆ диапазон(long long) Но идея, понятное дело, в обоих случаях одна и та же. И все эти типы могут быть различным с точки зрения диапазона. Как, впрочем, все они могу быть и одинаковыми с точки зрения размера и диапазона. То, что у вас на какой то платформе тип long совпал по представлению с типом in является лишь особенностью вашей платформы.

Ответ 4



Язык C предлагает Вам множество типов. В частности гарантированно, что int не больше long. А long не больше long long. Вы выбираете по своим нуждам и удобству. Когда тип используются часто, то компилятор оптимизирует работу с ним. После оптимизации в компиляторе, начинают рекомендовать использование типа для каких-то целей. На разных архитектурах ЭВМ разные размеры регистров процессора, но соотношение размеров типов сохраняется. Простыми словами: часто использовали int в программах, поэтому его вытянули под разме 4 байта на PC, что соответствует регистру процессора x32. long не стали вытягивать до 8 байт, потому что на x32 потребуется две команды процессору вместо одной. Но не удалять же long, ведь его используют какие-то программы.

IDE для C и C++


Одной из проблем при программировании является выбор IDE, предлагаю в этом топике разобрать какие IDE существуют для C и C++. В чем преимущество одного над другим.    


Ответы

Ответ 1



Visual Studio Express Плюсы: Бесплатность Подстановка имени функции по началу Возможность свернуть блок Минусы: Нельзя ресурсные файлы визуально редактировать Нет поддержки MFC/ATL Только Windows Visual Studio Community Плюсы: Бесплатность для определенных категорий пользователей (для разработчиков open-source или для небольших предприятий) Подстановка имени функции по началу Возможность свернуть блок Наличие редактора ресурсов, поддержка MFC/ATL Минусы: Платный продукт для крупных предприятий (>250 ПК или > 1 млн. долларов годовой доход) Только Windows Visual Studio (редакции Professional/Enterprise) Плюсы: Подстановка имени функции по началу Возможность свернуть блок Наличие редактора ресурсов, поддержка MFC/ATL Поддержка TFS Расширенный функционал тестирования Наличие плагинов Минусы: Платный продукт Только Windows CLion Плюсы: Рефакторинг Автоподстановка всего Кроссплатформенность Отладка через GDB Минусы: - платный продукт wxDev-CPP Плюсы: Бесплатность Наличие плагинов Минусы: Отсутствие подстановки имени функции по началу Eclipse CDT Плюсы: Бесплатность Плагины (невероятно много) Рефакторинг (не слишком крутой, правда) Автоподстановка любых идентификаторов Встроенная поддержка Doxygen Возможность отладки через GDB Кроссплатформенность Минусы: Code::Blocks Плюсы: Бесплатность Кроссплатформенность Поддержка множества компиляторов Поддержка пакетов от Dev-C++ Еженедельные nightly build Множество шаблонов приложения для разных библиотек (wxWidjets, Ogre3D, Irrlicht Opengl, SDL и т.д.) Поддержка Fortran 95 (иногда бывает очень полезно) Минусы: Редкие официальные релизы Нет подсветки ошибок в редакторе Слабая аналитика Полное отсутствие рефакторинга NetBeans Плюсы: Бесплатность Плагины Рефакторинг Автоподстановки методов, классов и макросов Возможность отладки, включая удалённую Кроссплатформенность Минусы: Недостаточная поддержка разных видов сборки Требует установки и подключения компиляторов KDevelop Плюсы: Бесплатный Автоподстановка кода (кое-где) Интересная подсветка кода Отладка через GDB Минусы: Иногда недостаточно умный анализ кода MinGW Developer Studio Плюсы: Бесплатность Минусы: TODO: указать минусы CodeLite Плюсы: Бесплатность Qt Creator Плюсы: Бесплатность Кроссплатформенность Поддержка множества компиляторов Автоподстановка всего Возможность свернуть блок Рефакторинг Отладка через GDB/CDB Работа с git "из коробки" MonoDevelop Плюсы: Бесплатность Минусы: Проект закрыт Visual Studio Code Плюсы: бесплатный много плагинов настраиваемый работоспособное автодополнение работа с git "из коробки" работает также под linux/mac (для с++ нужно поставить плагин от Майкрософт - Ctrl+P, ext install c++) Минусы: не умеет полноценно работать с кодом с++ под виндовс. Под linux/mac поддержка очень хорошая (включая отладчик)

С помощью чего лучше cделать Глобус+карту на веб странице?


Стоит задача:


Вращающийся 3d глобус, с отмеченными на нем метками в виде точек с подписями(например Москва, Англия и т.д.).
Они должны подгружаться из списка в файле cities.
При клике на него (Глобус) он должен трансформироваться в 2d карту с такими же метками
но именно в этой проекции при наведении на метку будет появляться подпись метки (в виде например количество населения и краткого списка архитектурных особенностей).
Так же при клике на метку или же на имя города чтоб перенаправляло на нужную страницу.  


С помощью чего это лучше делать?


SVG  
Canvas


Ни с Canvas, ни с SVG ранее не работал. Начал изучать canvas но столкнулся с те
что он не сможет обеспечить все что мне нужно. Примеров с глобусом нашел великое множество но нормальной документации не нашел (на нее тоже пните пожалуйста).
    


Ответы

Ответ 1



Прилагаю Вашему вниманию расширенную версию использования WebWorldWind Что тут представлено: Глобус и плоская карта в проекции меркатора. Звезды, данные взять из json. Какой-никакой рендеринг атмосферы. Информация о городах взята из json, нанесены маркеры на карту и добавлены события на клики к ним. Прикручен анализ того, что под мышкой + по клику фетчим инфу из википедии
use right mouse drag to pan
Вариация предыдущего сниппета, тут все города сложены в kd-дерево, и ищутся в нем ближайшие к месту клика с радиусом в один градус, затем наносятся на карту...
use right mouse drag to pan


Ответ 2



Есть много open source глобусов/карт для WEB, сделанных разными компаниями для разных целей. Перечислю те, с которыми приходилось работать, а вы уж выбирайте: Cesium Очень большой по количеству исходного кода и абстракций глобус, он больше ориентирова на realtime rendering. Очень много чего умеет из коробки, но без знаний компьютерно графики с ним может оказаться сложновато. Часто используется и можно найти много примеров про него. Хорошо утилизирует ресурсы процессора и видеокарты, в следствие чего его производительность на высоте.
Web World Wind Тоже глобус, намного проще внутри, помедленнее работает, чем Cesium, меньше реализованны фишек, легче поддаётся пониманию и кастомизации. Работает в один поток, вычисления на видеокарты не ускоряет, почти не использует шейдеры. let c = document.getElementById('canvasOne'); c.width = window.innerWidth; c.height = window.innerHeight; var wwd = new WorldWind.WorldWindow("canvasOne"); wwd.addLayer(new WorldWind.AtmosphereLayer()); wwd.addLayer(new WorldWind.BingAerialWithLabelsLayer()) var markers = new WorldWind.RenderableLayer("Markers") wwd.addLayer(markers); var attributes = new WorldWind.PlacemarkAttributes(); var position = new WorldWind.Location(60, 30) var placemark = new WorldWind.Placemark(position, /*eyeDistanceScaling*/true, attributes); attributes.imageOffset = new WorldWind.Offset( WorldWind.OFFSET_FRACTION, 0.3, WorldWind.OFFSET_FRACTION, 0.0); attributes.labelAttributes.color = WorldWind.Color.YELLOW; attributes.labelAttributes.offset = new WorldWind.Offset( WorldWind.OFFSET_FRACTION, 0.5, WorldWind.OFFSET_FRACTION, 1.0); placemark.label = "Saint-Petersburg"; placemark.altitudeMode = WorldWind.CLAMP_TO_GROUND; placemark.eyeDistanceScalingThreshold = 2500000; attributes.imageSource = WorldWind.configuration.baseUrl + "images/pushpins/plain-red.png"; markers.addRenderable(placemark);; body { margin:0; overflow:hidden; } D3.js Очень мощная штука, больше подходит для SVG, но и по-другому можно отрендерить, содержи математику для множества географических проекций и их расширения, отлично подходит для создания 2D карт и карт во всяких экстравагантных проекциях, отличных от mercator/wgs84, которые используются почти во всех других картографических движках. var width = 750, height = 500; var options = [ {name: "Aitoff", projection: d3.geoAitoff()}, {name: "Albers", projection: d3.geoAlbers().scale(145).parallels([20, 50])}, {name: "August", projection: d3.geoAugust().scale(60)}, {name: "Baker", projection: d3.geoBaker().scale(100)}, {name: "Boggs", projection: d3.geoBoggs()}, {name: "Bonne", projection: d3.geoBonne().scale(120)}, {name: "Bromley", projection: d3.geoBromley()}, {name: "Collignon", projection: d3.geoCollignon().scale(93)}, {name: "Craster Parabolic", projection: d3.geoCraster()}, {name: "Eckert I", projection: d3.geoEckert1().scale(165)}, {name: "Eckert II", projection: d3.geoEckert2().scale(165)}, {name: "Eckert III", projection: d3.geoEckert3().scale(180)}, {name: "Eckert IV", projection: d3.geoEckert4().scale(180)}, {name: "Eckert V", projection: d3.geoEckert5().scale(170)}, {name: "Eckert VI", projection: d3.geoEckert6().scale(170)}, {name: "Eisenlohr", projection: d3.geoEisenlohr().scale(60)}, {name: "Equirectangular (Plate Carrée)", projection: d3.geoEquirectangular()}, {name: "Hammer", projection: d3.geoHammer().scale(165)}, {name: "Hill", projection: d3.geoHill()}, {name: "Goode Homolosine", projection: d3.geoHomolosine()}, {name: "Kavrayskiy VII", projection: d3.geoKavrayskiy7()}, {name: "Lambert cylindrical equal-area", projection: d3.geoCylindricalEqualArea()}, {name: "Lagrange", projection: d3.geoLagrange().scale(120)}, {name: "Larrivée", projection: d3.geoLarrivee().scale(95)}, {name: "Laskowski", projection: d3.geoLaskowski().scale(120)}, {name: "Loximuthal", projection: d3.geoLoximuthal()}, // {name: "Mercator", projection: d3.geoMercator().scale(490 / 2 / Math.PI)}, {name: "Miller", projection: d3.geoMiller().scale(100)}, {name: "McBryde–Thomas Flat-Polar Parabolic", projection: d3.geoMtFlatPolarParabolic()}, {name: "McBryde–Thomas Flat-Polar Quartic", projection: d3.geoMtFlatPolarQuartic()}, {name: "McBryde–Thomas Flat-Polar Sinusoidal", projection: d3.geoMtFlatPolarSinusoidal()}, {name: "Mollweide", projection: d3.geoMollweide().scale(165)}, {name: "Natural Earth", projection: d3.geoNaturalEarth()}, {name: "Nell–Hammer", projection: d3.geoNellHammer()}, {name: "Polyconic", projection: d3.geoPolyconic().scale(100)}, {name: "Robinson", projection: d3.geoRobinson()}, {name: "Sinusoidal", projection: d3.geoSinusoidal()}, {name: "Sinu-Mollweide", projection: d3.geoSinuMollweide()}, {name: "van der Grinten", projection: d3.geoVanDerGrinten().scale(75)}, {name: "van der Grinten IV", projection: d3.geoVanDerGrinten4().scale(120)}, {name: "Wagner IV", projection: d3.geoWagner4()}, {name: "Wagner VI", projection: d3.geoWagner6()}, {name: "Wagner VII", projection: d3.geoWagner7()}, {name: "Winkel Tripel", projection: d3.geoWinkel3()} ]; options.forEach(function(o) { o.projection.rotate([0, 0]).center([0, 0]); }); var i = 0, n = options.length - 1; var projection = options[i].projection; var path = d3.geoPath(projection); var graticule = d3.geoGraticule(); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); svg.append("defs").append("path") .datum({type: "Sphere"}) .attr("id", "sphere") .attr("d", path); svg.append("use") .attr("class", "stroke") .attr("xlink:href", "#sphere"); svg.append("use") .attr("class", "fill") .attr("xlink:href", "#sphere"); svg.append("path") .datum(graticule) .attr("class", "graticule") .attr("d", path); d3.json("https://unpkg.com/world-atlas@1.1.4/world/110m.json").then(function(world) { svg.insert("path", ".graticule") .datum(topojson.feature(world, world.objects.land)) .attr("class", "land") .attr("d", path); }); var menu = d3.select("#projection-menu") .on("change", change); menu.selectAll("option") .data(options) .enter().append("option") .text(function(d) { return d.name; }); update(options[0]) function loop() { var j = Math.floor(Math.random() * n); menu.property("selectedIndex", i = j + (j >= i)); update(options[i]); } function change() { clearInterval(interval); update(options[this.selectedIndex]); } function update(option) { svg.selectAll("path").interrupt().transition() .duration(1000).ease(d3.easeLinear) .attrTween("d", projectionTween(projection, projection = option.projection)) d3.timeout(loop, 1000) } function projectionTween(projection0, projection1) { return function(d) { var t = 0; var projection = d3.geoProjection(project) .scale(1) .translate([width / 2, height / 2]); var path = d3.geoPath(projection); function project(λ, φ) { λ *= 180 / Math.PI, φ *= 180 / Math.PI; var p0 = projection0([λ, φ]), p1 = projection1([λ, φ]); return [(1 - t) * p0[0] + t * p1[0], (1 - t) * -p0[1] + t * -p1[1]]; } return function(_) { t = _; return path(d); }; }; } body { background: #fcfcfa; height: 500px; position: relative; width: 960px; } #projection-menu { position: absolute; right: 10px; top: 10px; } .stroke { fill: none; stroke: #000; stroke-width: 3px; } .fill { fill: #fff; } .graticule { fill: none; stroke: #777; stroke-width: .5px; stroke-opacity: .5; } .land { fill: #222; } .boundary { fill: none; stroke: #fff; stroke-width: .5px; } Mapbox-gl-js Особенность этой пркекрасной штуки - реалтайм рендеринг векторных тайлов в 2D и 3 режиме(в 3D не глобус, а плоская карта под углом, как columbusview в cesium), но с растровыми источниками данных оно тоже работать умеет. Проекцию толком поменять нельзя. mapboxgl.accessToken = 'pk.eyJ1Ijoib3dlbmxhbWIiLCJhIjoiY2lleWljcnF4MDBiOXQ0bHR0anRvamtucSJ9.t3YnHHqvQZ8Y0MTCNy0NNw'; this.map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/streets-v9', center: [30.3, 60], zoom: 11 // starting zoom }); // Add zoom and rotation controls to the map. var nav = new mapboxgl.NavigationControl(); map.addControl(nav, 'top-right'); body { margin: 0; padding: 0; } #map { position: absolute; top: 0; bottom: 0; width: 100%; }
Leaflet Маленькая да удаленькая библиотека для 2D карт. Не использует на прямую видеокарту и весит очень скромно, при этом имеет большое коммьюнити и множество всяких разных расширений. Хорошо подходит для мобильных карт и слабых компьютеров. Поддерживает различные проекции. var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'; var osmLayer = new L.TileLayer(osmUrl, { maxZoom: 18 }); var baseMaps = {"OpenStreetMap": osmLayer}; var map = new L.Map('map', { center: new L.LatLng(60, 30.3), zoom: 12, layers: [osmLayer] }); body { margin: 0; overflow: hidden; } #map { position: fixed; width: 100%; height: 100%; }
Openlayers Отличная альтернатива leaflet, больше исходного кода, больше фич, больше порог вхождения, много примеров, нормальная поддержка различных проекций. 2D. new ol.Map({ layers: [new ol.layer.Tile({ source: new ol.source.OSM() })], target: 'map', view: new ol.View({ center: ol.proj.fromLonLat([30.3, 60]), zoom: 12 }) }); body { overflow: hidden; margin: 0; } #map { position: absolute; height: 100%; width: 100%; }
PS: буду дополнять этот пост по мере сил... UPD 01.01.2019: изменил пример на cesium и 2 картинки на gif UPD 01.01.2019: изменил пример на worldwind и 1 картинку на gif

Ответ 3



Если нужен именно глобус а не карта, то можно использовать такую связку: Leaflet + WebGL Earth 2.0, которая расширяет базовые возможности Leaflet и добавляет 3D Плюсом такого решения является то, что при таком подходе доступны все инструмент Leaflet. К примеру для работы с метками, их кластеризация, обработка кликов, отображение атрибутивной информации. Плюс появляется возможность показа в 3D. Поддерживает различные стили от MapBox: Также есть возможность создавать собственные стили. Данные могут быть получены с серверов MapBox или с собственных гео-серверов, а также из файлов GeoJson или csv. Ссылки: GitHub WebGL Earth 2.0 https://github.com/webglearth/webglearth Документация и API WebGL Earth 2.0 http://www.webglearth.org Пример WebGL Earth 2.0 https://www.webglearth.com Документация API и примеры Leaflet https://leafletjs.com

Ответ 4



Отдельный пост посвященны Cesium. Тут нанесен topojson с границами субъектов федерации РФ, и города с кластеризацией. var cities = 'https://raw.githubusercontent.com/lutangar/cities.json/master/cities.json'; var russia = 'https://raw.githubusercontent.com/logvik/d3_russian_map/master/map_assets/russia_mercator.json'; setInitialView(50, 60); var imagery = Cesium.createDefaultImageryProviderViewModels().slice(3); var viewer = new Cesium.Viewer("cesiumContainer", { timeline: false, animation: false, geocoder: false, imageryProviderViewModels: imagery }); let ds = new Cesium.CustomDataSource('billboards'); viewer.dataSources.add(ds); clusteringSupport(ds); var canvas = createCityImage(); viewer.dataSources.add(Cesium.GeoJsonDataSource.load(russia, { stroke: Cesium.Color.RED, fill: Cesium.Color.TRANSPARENT, strokeWidth: 5, markerSymbol: '?' })); fetch(cities).then(r => r.json()) .then(r => r .filter((c, i) => c.country === "RU") // .filter((c, i) => i % 100 === 0) .forEach(addCity)) addMouseover(); //// var pickedObject; window.addEventListener('click', function() { pickedObject&&console.log(pickedObject.id._name) }) function addMouseover() { new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas) .setInputAction(function(movement) { pickedObject = viewer.scene.pick(movement.endPosition); }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); } function setInitialView(lon, lat) { Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(lon-10, lat-10, lon+10, lat+10); Cesium.Camera.DEFAULT_VIEW_FACTOR = 0; } function addCity(city) { var pos = Cesium.Cartesian3.fromDegrees(+city.lng, +city.lat); ds.entities.add({ name : city.name, position: pos, description: `${city.name}`, billboard: { image: canvas }, label : { text : city.name, font : '14pt monospace', style: Cesium.LabelStyle.FILL_AND_OUTLINE, outlineWidth : 2, verticalOrigin : Cesium.VerticalOrigin.BOTTOM, pixelOffset : new Cesium.Cartesian2(0, -9) } }) } function clusteringSupport(ds) { ds.clustering.enabled = true; ds.clustering.pixelRange = 35; var pinBuilder = new Cesium.PinBuilder(); var amounts = [2000, 1000, 500, 100, 50, 10, 9, 8, 7, 6, 5, 4, 3, 2].map(i => ({ i: i, image: pinBuilder.fromText(i + (i>9 ? "+" : ""), Cesium.Color[i>100?'RED':i>10?'BLUE':'GREEN'], 38).toDataURL() })); ds.clustering.clusterEvent.addEventListener(function(clusteredEntities, cluster) { cluster.label.show = false; cluster.billboard.show = true; cluster.billboard.id = cluster.label.id; cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM; for (var i=0; i= amounts[i].i) { cluster.billboard.image = amounts[i].image; break; } } }); } function createCityImage(){ var canvas = document.createElement("canvas"); canvas.width = 16; canvas.height = 16; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.arc(5, 5, 5, 0, Cesium.Math.TWO_PI, true); ctx.closePath(); ctx.fillStyle = "red"; ctx.fill(); return canvas; } .cesium-widget-credits{ display:none !important; /* only for example on ru.so*/ }
PS: фич у этого глобуса достаточно, буду дополнять этот пост иногда...