Pull to refresh

Comments 66

А кому сейчас нужны Desktop developer-ы, тем более junior? И вроде на голом Winapi сейчас никто не пишет, берут Qt или не дай бог какой-нибудь MFC.

И вроде на голом Winapi 

ну если вроде то такие я думаю никому не нужны (Т.Т)

На голом WinAPI тоже пишут, но весьма не много, и как правило когда редактируют куски какого-либо фреймворка, который на нижнем уровне все равно будет иметь доступ к WinAPI.

Другой вопрос, что это примерно худший гайд по основам языка и созданию UI приложений, который я видел. Автор, пожалуйста, выучи сначала что-то сам, прежде чем пытаться учить этому других.

Ты прав в том, что в сущности программирование — композиция абстракций от элементарного уровня до очень сложного. Однако это не все, что нужно понимать. Еще очень важно понимать и знать общепринятые паттерны, алгоритмы и структуры данных, которые повсеместно используются для этой композиции. Инженер при работе не думает о том, как решать простые задачи, он знает типовые решения и применяет их. И другой инженер, читая код спустя пол года, с пары взглядов узнает паттерны и понимает, что делает этот код. Не менее важно знать инструменты, которые ты используешь. C++ не тот язык, который можно освоить прочитав одну статью за пол часа. Уходят месяцы и годы, чтобы понять большинство его аспектов.

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

Некоторые из ошибок которые бросились в глаза:

  1. #pragma once - стоит выучить, что это и зачем нужно. И почему это не нужно добавлять в .cpp файлы (хорошие компиляторы с правильными настройками напишут предупреждение про это).

  2. using namespace std - плохая привычка. И нет, это совсем не другой способ импорта.

  3. #define - очень плохой с точки зрения C++ способ объявлять константы.

  4. Глобальные переменные — антипаттерн. Невозможно нормально контроллировать, какая часть программы и когда их поменяет, что приводит к нежелательным эффектам тогда, когда ты этого не ожидаешь. Также логику, использующую глобальные переменные бывает очень тяжело и неприятно тестировать. Крайне редко, без них нельзя обойтись, в норме же от них надо избавляться.

  5. Есть веские причины, почему практически всегда свойства класса помещают в private: секцию, или по крайней мере в protected:. Если этого не нужно, возможно не нужен сам класс.

  6. Любому new должен соответствовать delete, а в современном C++ ни того, ни другого присутствовать не должно. Есть умные указатели, которые помогают управлять памятью.

  7. Использовать #include на .cpp файлах — в корне не правильно и приводит к ошибкам линковки.

Я согласен что наш автор картошка, но вот эти все аргументы ну как бы...

  1. #pragma once вообще не стоит использовать, если подразумевается использовать другой компилятор. Так как это всё ещё "фича", а многие gcc ориентированные компиляторы будут ругать на него, если не "чекнуть флаги"

  2. Использовать using namespace можно, но только внутри каких-то функций, .срр файлов или при специфичной организации кода.

  3. Согласен с #define, но никто не объясняет почему...

  4. Глобальные переменные это нормальная организация, но которая влечёт за собой тьму проблем из-за невнимательности при реализации. Обычно этот подход обрамляется специфичным поведением которое и позволяет контролировать доступ к переменной. Если бы это было антипаттерном, никто бы не пользовался фабриками и всё передавали бы по ссылке/указателю, как это любят делать в С.

  5. Нету никаких причин обязывать людей писать class с приватными полями. Тоесть ты подразумеваешь, что если объект состоит только из публичных полей то это struct иначе class? Напомню просто, что структуры, изначально, просто способ запихнуть несколько типов в один, а вот классы как раз и интегрировали в себя методы и права доступа. В целом это вкусовщина. Вон UnrealEngine весь на структурах написан, чот я холиваров не слышал по этому поводу.

  6. В пользовательском коде не должно быть new/delete- в такой формулировке согласен. Этим всем должно заниматься окружение, а если сильно хочется то только через смартпоинты.

  7. Чисто технически #include просто копирует файл в текущий, так что как бы можно... Вопрос нахера?

  1. по факту подавляющее большинство современных компиляторов поддерживают #pragma once без проблем. Но например у gcc и clang есть предупреждение про использование в .cpp файле, которое, к тому же включено по умолчанию:

alex-ac@alex-ac-mbp /tmp $ echo '#pragma once' >test.cc
alex-ac@alex-ac-mbp /tmp $ clang++ -c test.cc
test.cc:1:9: warning: #pragma once in main file [-Wpragma-once-outside-header]
#pragma once
        ^
1 warning generated.
  1. да, согласен, но не бездумно везде. На самом деле эта рекомендация может уйти прошлое с модулями. Главное чтобы никто не начал писать export using namespace std;. И вообще, какой-нибудь using namespace std::placeholders; внутри функции бывает очень полезен.

  2. Элементарно же. #define - просто подставляет текст. нет информации о типах, есть вероятность, что будет парситься не так, как этого ожидаешь если рядом какие-то сложные выражения. Объявление константы добавляет сущность, которая имеет явный тип, на которую распространяется ODR, на которую зарезервировано место в бинарнике, etc. Гораздо меньше вариантов выстрелить себе в ногу. Есть два повода писать #define в современном C++ - если объявленное значение нужно использовать в #if, или если объявляется макрос с параметрами, который позволяет значительно сократить написание повторяющегося кода. Причем, последнее спорно. В целом, чем дальше развивается язык, тем меньше становится потребность в препроцессоре.

  3. Да, глобальные переменные ок для некоторых задач. Но для общего назначения это антипаттерн.

  4. Все ради инкапсуляции. Все, что находится в приватной секции не является интерфейсом класса (за исключением того, как это влияет на размер объекта и vtable). Декларируя в публичной секции поля мы декларируем эти поля частью интерфейса класса и лишаем себя возможности инкапсулировать логику связанную с изменением значений этих полей. Когда мы прячем поля в приватной секции и добавляем к публичному интерфейсу геттер/сеттер, мы оставляем возможность инкапсулировать любую логику в сеттер или делегировать хранение значение в какой-то другой объект/объекты. В Visual C++, objective-c, swift, C# и JS есть property, которые позволяют сделать property частью публичного интерфейса, при этом оставляя возможность инкапсулировать логику по изменению их значений (и по факту являются синтаксическим сахаром для гет/сеттеров. Но в стандартном C++ этого нет, поэтому оправдано убирать все в приватную секцию всегда. И да, если нет необходимости в инкапсуляции данных, возможно действительно нужны структуры, которые сейчас, по факту, — те же классы, имеющие только публичную секцию.

  5. Ну вот да, в современном C++ видеть new/delete без реальной на то необходимости — плохо. Аллокация на стеке — гораздо лучше. Если надо в куче — make_unique/make_shared, пожалуйста.

  6. Нет. Во-первых, есть компиляторы, которые на такое ругаются при достаточно параноидальных настройках. Во-вторых, многие билд-системы будут делать предположение, что .cpp файл — это отдельная единица трансляции, которая должна быть скомпилирована в объектник и добавлена на этапе линковки. Включение целиком одного .cpp файла в другой приведет к тому, что все символы в этом файле будут объявлены в двух единицах трансляции. И если для какой-то части из них это не имеет значения благодаря ODR, то в большинстве случаев это приведет к ошибкам линковки этих символов. По крайней мере к предупреждениям о том, что это случилось и линковщику пришлось выбирать между ними.

Я намеренно не хотел расписывать подробно по каждому из пунктов, почему это так. У автора явно все слишком плохо с знанием языка, чтобы понимать причины и концепции на которых основаны эти объяснения, а те, кто знают язык и без объяснений про это знают. Моей целью было указать на ошибки, чтобы автор сам изучил эти вопросы и усвоил это лучше, чем если бы ему сказали ответ, который он все равно до конца не понимает.

В unreal engine обычно в CPP файл и подключают #include.

В h файле лишь пару #include для подключения ядра (вроде бы, точно не помню) и сгенерированного кода, если нужен какой-то класс, то в h файле объявил, в CPP подключил

Речь идёт не о включении в cpp файл (что нормально), а о включении одного cpp файла в другой (что бред)

И ещё раз...

Стандарт этого не запрещает, но поведение "оставляет за реализацией". У кого-то работает, а кто-то может и на C++ ругаться в .h файлах ведь надо писать .hpp. Каждый развлекается как хочет.

Лично я просто люблю истину искать и рассказывать всем подряд какой "чудесный" язык я изучаю. И если давать советы чо и как делать, не лучше ли вливать больше конкретики? Это может и перегрузит несчастного, но даст ему довольно качественный отзыв о его труде, не?

  1. Большая поддержка ещё не повод говорить о том что это стоит использовать. Если вам недайбог придётся вникать в компиляторы для микроконтроллеров, то узнаете много нового)

  2. Надеятся на поддержку современных версий стандарта тоже не самая лучшая идея) Многие всё ещё сидят на 14 стандарте, и даже msvc всё ещё сидит на 20 стандарте, когда уже вроде 23 утвердили, какбы? (А gcc лагает)

  3. Ну да... Когда вместо constant::Pi is not constant! видишь 3.14 is not constant! , то как-то не по себе становится... Где ошибку то искать?))

  4. ...ок

  5. Очень интересное описание проблемы. Но опять же, это всё вкусовщина. Вообще я не люблю холивары вокруг ООП, потому что они "инкапсулируют" в себе реальную проблему которую должен решать программист.)

  6. ...ок

  7. Стандарт не говорит про то как должен быть реализован #include. С этой точки зрения он должен просто копировать файл согласно некоторым правилам. Но никто не запрещает тебе сделать компилятор который бы запрещал включение .h файла с С++ кодом и просил тебя поменять расширение на .hpp. +Ситуация с #include "x.cpp" не должна приводить к ошибкам линковки, это всё ещё часть стандарта)) Просто в таком случае выбор символа зависит "от реализации", что для программиста зачастую означает - рандом. Хотя какую именно мы ситуацию рассматриваем не совсем понятно мне.

Ситуация с #include "x.cpp" должна приводить к ошибкам линковки если тот файл, который включает в себя x.cpp, и сам x.cpp линкуются вместе, а в x.cpp содержится хотя бы 1 не-мягкое глобальное определение. То есть почти всегда.

И вроде на голом Winapi сейчас никто не пишет, берут Qt или не дай бог какой-нибудь MFC.

Писать вы можете на чем угодно, но знать API платформы (если вы, конечно, не тот самый "мамкин программист") все-таки нужно.

Иначе можно немножко оконфузится - один такой "товарисч" после пары дней боданий с фреймворком мне с пеной у рта доказывал что "вот так сделать невозможно потому что этого в фреймворке не предусмотрено" (конкретики уже не помню, что там было, какой фреймворк был - скорее все это бы VCL и Borland C++ Builder). На что я ему "это" написал на WinAPI минут за 15.

Вообще, в WinAPI есть много такого, что вроде бы и не каждый день нужно (и в фреймворки это никто не затащит), но иногда бывает полезным. Так что лазать в MSDN очень рекомендуется.

Что, впрочем, не отменяет поверхностность данной статьи. Начать с того, что нет внятного объяснения как устроено GUI приложение Windows (очередь сообщений, чтение сообщений, диспетчер сообщений, оконные функции обработки сообщений и вообще что все построено на сообщениях) - все это можно обрисовать и без кода. А потом уже, когда понятна общая структура, тогда уже код.

Ну, для общего развития и для понимания того, как оно работает, неплохо уметь немного работать на голом API, без использования сторонних библиотек и т.п. Но вряд ли это оправдано для реальной разработки, конечно.

Чтобы понять как оно работает, такую учебную программу лучше всего написать на pure C и голом WinAPI. И еще с разными типами контролов побаловаться. А потом уже все остальное.

Что не так с Desktop developer-и?

Спасибо за статью! Я даже не успел дочитать ее, а мне уже позвонил HR и принял в компанию на зп 600к в месяц!

Вы зря не дочитали статью до конца прежде чем согласится, потеряли 400 тысяч /месяц

Я не жадный и ленивый, меня все устроило

я потратил 4 вечера на эту статью

Напрасно.

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

Даже типично-советский трудовик в моей школе был более серьёзен. Мог, конечно, и подзатыльник дать, и молотком замахнуться -- но по делу, за несоблюдение ТБ, например. Но никогда не объяснял тему в стиле "берём вот эту хреновину аххахаха..."

У нас трудовик, в частности, терпеть не мог, когда отверстия называли дырками :) Ну, как-то я так назвал, он типа наехал, а я ему говорю: мол, сами гляньте: края рваные, форма не пойми какая... (отверстие я действительно жутко кривое тогда сделал). Он подошёл, посмотрел и говорит: "Да, ты, прав, это не отверстие, это дырка" :)

Ну а насчёт оценки стиля статьи полностью согласен. Писать надо серьёзно, а не just for lulz (серьёзно -- не значит занудно).

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

Не знаю, мне бы в середине 90-х такое понравилось. И по стилю подачи, и по обсуждаемой теме.

Увы, как активно ищущий сейчас работу, могу сказать, что 99+% предложений работы связаны с микросервисами на облаках, большими модными базами данных и прочими Докерами. Никаких нативных/десктопных программ и на фиг никому не нужно, увы (сам большой спец по десктопу).

UFO just landed and posted this here

Можно еще посмотреть в сторону wxWidgets.

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

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

Присоединяюсь к вашей скорби и негодованию, шлю лучи добра.

И почему статьи такого рода всегда пишут люди, у которых в школе был кол по русскому?

Ну вот зачем так? Человек, который приучается использовать вещи, плохо понимая и не пытаясь вникнуть в то, что они из себя представляют (как #pragma и #define в Вашем случае), это не программист, а бомба замедленного действия: он чего-то сляпает, посмотрит - вроде работает, а потом у людей недели уйдут, чтобы понять, в чем проблема.

У меня был такой случай. Была аппликация на Java SWT. Это на 99% процентов дейстаительно платформно-независимое, но для какой то штуки понадобилось получить handler окна и чего-то с ним сделать. И вот настало время поддерживать 64 бита наряду с 32. Так девочка, которая этим занималась, просто тупо сделала casting, что-то типа:

HWND h = (int32)this.GetWindowHandle();

Это, как понимаете, чтобы гарантированно работало в обоих случаях.

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

тупо сделала casting,

Но зачем? HWND он же и на 64 битах HWND?

Потому что в пересказе ошибка. Нет в Java никакого HWND.

Есть только handle, который int на одних платформах и long на других.

Да, так оно и есть. Это было давно, а я уже несколько лет Java не занимался.
Исправлю.

@randomsimplenumber Опции исправить я не вижу, значит придется писать отдельно.
Изначально было примерно так:
int h = this.GetWindowHandle();
Потом выяснилось, что с 64 бит - ошибка присвоения типа.
И девочка ее "исправила":
int h = (int)this.GetWindowHandle();

РЕМАРКА! Эта статья не будет интересна тем, кто уже знает программирование, поэтому попрошу избавить меня от вашего «экспертного мнения» программиста.

Эта статья не будет интересна никому, поэтому попрошу избавить всех от вашей «экспертной статьи».

Очень плохая привычка говорить за всех.

А подскажите ньюбу! Я скачал Visual Studio, какой то там новой версии, и она сказала мне зарегистрироваться, иначе работать не будет. Ну зарегистрировался, паспорт не спросили, только почту.. работает. Даже без интернета.. а потом перестала - запросила интернет. Спустя какое то время. И, типа, проверила логин, и чё то ему не понравилось: типа страна .. не работаем тут.. короче лесом послала. Ичемнетеперь ? как ее использовать?

Это с каких пор VS не работает в "какой то" стране? Пользуюсь ежедневно и чёт проблем не наблюдаю.

Ну вот я не знаю, у меня залокировалось на учетке.. где то год назад еще.. типа free edition была с сайта микрософта в общем то. поэтому и спрашиваю

Самое близкое к тому, что может соответствовать вашим словам: вы скачали демо платной версии, заблокировалась она по истечению демо-периода, а "неправильная страна" написали уже в окне покупки лицензии. Пользуйтесь community edition и будет вам счастье.

спасибо, но нет. vs2015.3.com_enu.iso - community edition  2015 года и учетка гвоздями прибита была

UFO just landed and posted this here

Спасибо, я решил вспомнить че у меня случилось и есть два момента

1) качал то я с comuniti edition но мне нужен был offline инсталятор, в результате я поставил только VS2015 comuniti edition (vs2015.3.com_enu.iso) с заведением и введением учетки MS

2) Учетку мне действительно заблокировали, но ошибся в причине, вот что пишет

Ваша учетная запись заблокирована

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

Разблокирование учетной записи

Нажмите Далее, и мы отправим код проверки на ваш телефон. После ввода кода вы сможете вернуться в свою учетную запись.

Знакомая история. Телефон их мне им сдалось вводить .

Вопрос такой: для VS2022 CE мне тоже учетка будет нужна?

Вы вместо бесплатной версии скачали триал платной.

Решение тут простое - удалить студию и скачать VS Community Edition.

Вопрос такой: для VS2022 CE мне тоже учетка будет нужна?

Нет. Она тоже попросит учётку, но это окно можно просто закрыть.

Эх, и влепил бы статье минус, да комментов жалко )

Даже у таких статей на хабре есть плюс - в комментах можно узнать много нового о языках и фреймворках))

Снова как окунулся в журнал ][Акер 2000 года выпуска. Ждём от автора статьи как ломать аську и где разжиться халявным инетом.

Какая-то не смешная юмореска (это же юмореска, да???)

Поздравляю, теперь ты полноценный Junior C++ Desktop Developer, если сверху изучить работу с памятью и работу с базами данных, будешь уже Middle C++ Engine Developer,

вот вы пошутили (вы же пошутили, да??? пошутили???), а кто-то с этим на собеседование придет! Зачем вы так?

Я тоже вначале подумал, что автор просто толстый тролль, но прочитав половину этого "труда", понял, что все намного проще...

Без обид, уважаемый автор, но вы сами плохо знаете С++. У вас, как минимум, нет понимания того, что такое препроцессор и для чего нужны его команды, иначе ничем нельзя объяснить некоторые части вашего кода.

UFO just landed and posted this here

Создать Button или Label много ума не нужно, а что дальше?

А на чем сейчас эффективно писать десктоп-приложения, кроссплатформенные,
по уровню как какой-нибудь "мой офис"?

Вот знаю Delphi, но громких проектов новых на ней не слышно.

UFO just landed and posted this here

А где слышно о громких десктоп проектах вообще?

> Как войти в айти за час с нуля, на языке с++ Win32 (графические приложения) «для самых маленьких»

Это называется: «Дайте мне любую, высокую и светлую идею и я ее... испохаблю!»

На самом деле С++ и WinAPI это очень классные вещи,  но такая подача вызывает естественный вопрос: «А на фейхуа это мне надо?».

Лично я считаю, что программирование лучше демонстрировать с конца. Т.е., показываете, допустим, какую-нибудь готовую программу и спрашиваете: «Вы желаете заниматься этим?». Или, как вариант, ставите задачу, которую собираетесь решить и говорите: «Вы хотите иметь программу, которая делает подобное?». А то, все любят учить начинающих, поскольку для этого много ума не надо, но никто не хочет учить уму-разуму «кончающих».

Но вернемся к C++ / WinAPI. Могу рассказать свой опыт. Пробовал Ассемблер (и даже дизассемблер, см. мой сайт http://erfaren.narod.ru/ - тогда наш регион был украинским, сейчас в составе РФ), Си, С++, WinAPI, Qt, wxWidgets, Win32++, WTL. Отдельно можно поговорить о других языках программирования: Visual FoxPro, Java, Python. В последнее время выбрал для себя C++ / WTL, потом успел разочароваться в WTL и перешел на C++ / WinAPI. Однако, к моему удивлению, сейчас у меня возникла вторая любовь к WTL. Это очень небольшая библиотека, в исходных кодах, но достаточно полезная для десктопного программирования с ориентацией на пользовательский интерфейс (GUI). Просто она достаточно глубокая и мощная для своего класса, что не слишком очевидно, пока не начнешь писать на ней реальные задачи.

А эволюция перехода от одного инструмента программирования к другому определялся теми задачами (в качестве хобби), которые я решал. Из последних тем, это создание обучающей программы «Scholium» («Сколиум»), v. 2.1 (для изучения иностранных языков - http://scholium.webservis.ru/ ) и программы «MediaText» подготовки данных для нее. Обе написаны на C++ / WTL, но последняя пока не опубликована, поскольку нужна только для первой программы, которая еще «не пошла в массы». Но ее скриншот я могу привести здесь.

http://scholium.webservis.ru/Pics/MediaText.png

А вот пример незаконченной версии обучающей программы, написанной на чистом C++ / WinAPI.

http://scholium.webservis.ru/Pics/CellsEdit.png

Исходя из данной информации, уже можно ставить вопрос новичкам: "Вы хотите этим заниматься?" Если, да, то далее начинаем осваивать WinAPI с WTL'ем или без, либо посылаем всё "на хутор бабочек ловить" и занимаемся своим вебом, как это делает большинство современных программистов.

Кстати, в "МедлиаТексте" самое трудное было внедрить в программу медиа-проигрыватель (опенсорсный FFPlay.c). Вот бы еще, по аналогии с этим внедрить с свою программу чистые листы Эксел, без лишних прибамбасов, то вообще было бы здорово, но это уже будет другая задача.

WinAPI, WTL... На дворе 2023 год, вы бы еще MFC вспомнили

WinAPI, WTL... На дворе 2023 год, вы бы еще MFC вспомнили

Вспомнил бы, если бы не забыл. В свое время я опубликовал несколько статей по своим MFC-проектам на codeproject.com. Уже тогда столкнулся  с резким отторжением народа от MFC. Поначалу это меня удивляло, ну да, чересчур изощренная библиотека, однако, героическое преодоление препятствий в ней, хорошо развивает мозги. Потом, согласился с общим мнением, что "таки да", зачем уподобляться ёжикам, которые "плакали, кололись, но ели кактус"?

А вот с другими утверждениями, типа "'восьмерка' лучше 'семерки', по определению" (это про 1С), "Qt лучше  wxWidgets", "wxWidgets лучше  Win32++ и WinAPI", " Win32++ лучше, чем MFC", "Python лучше  C++" и т.д. и т.п., это уже из области "А поговорить!" (в переводе на литературный язык). Примерно, как в анекдоте: "Армяне лучше, чем грузины! - Чем, чем лучше? - Чем грузины!"

Так и в вашем случае. Достаточно просто сказать, это же очевидно, "хайли лайкли"! Обоснования? Какие обоснования? "Джентльменам принято верить на слово!".

Могу вам сказать по секрету, утверждения, без обоснования обычно просто игнорируются, так что можно не утруждать себя их написанием либо высказыванием. Хотите поспорить? Тогда скажите, что-то вроде, а я, вот, написал свой вариант "МедиаТекста" на языке "Х" и фреймворке "Y", за считанные дни и лучшего качества. Либо дайте ссылку на того, кто это сделал лучше и быстрей на другой платформе.

А так, я прокрутил в голове все варианты, опыта, Слава Богу, хватает и понял, что нужную мне программу, в нужном качестве и характеристиками, быстрее и проще сделать на С++ / WTL. В любом случае аргументированной критики все равно не будет, а голословная мне не интересна...

Как толсто, но никто не оценил )

Ощущение, что попал обратно в нулевые.

Или это такой толстый вброс?

"Вот девушка красивая в кустах лежит нагой. Другой бы изнасиловал, а я лишь пнул ногой" (с)

В целом, хватило названия... К сожалению, встречаются люди, верящие подобному. Они даже деньги платят за всякие курсы, обещающие с нуля и до звёзд всего за пару месяцев. Нехорошо, стыдно это.

UFO just landed and posted this here
Sign up to leave a comment.

Articles