LINUX.ORG.RU

[C++?] Серьезный вопрос.


3

2

Просьба ответит серьезно, желательно с аргументами за или против.

Предистория:
Когда то давным давно (я тогда еще только закончил 9-ый класс) я увидел в газете объявление о наборе в летнюю группу по изучению классического программирования. В тот момент я был с компьютером на ты и "очень" хорошо в них разбирался (переустанавливал Windows каждый месяц, хаял Microsoft просто потому, что после моих настроек W приходилось постоянно переустанавливать). Группа по классическому программированию так и не набралась, но набралось 1 человек на Visual Basik for Applications. Я соглсился быть вторым и начались занятия.
Все, что мне там объясняли я схватывал быстро. Меня пригласили продолжить обучение в сентябре на курсе "моделирование".
Там уже был Pascal, который я тогда совсем не знал. Сам курс был очень разношорстный: мы изучали и использование мыши через прерывание, готовились к различным олимпиадам. Параллельно я изучил Pascal.
Потом был Delphi. К концу 10-го класса я уже неплохо владел приемами программирования и вовсю клепал бесполезные программулины. Потом поступил в универ на программиста. Там тоже был Delphi, и я особо не напрягаясь писал все лабы (к моменту поступления я уже был знаком с логикой указателей, самописные стеки и графы, etc).
На 2-ом курсе в гостях у знакомого я разобщался с человеком, который уже насколько лет работал в нерезиновой программистом. Он мне и открыл глаза на мир: "Delphi здох. Его уже похоронили и забыли. Сейчас необходимо знание C++, C#. Необходимо занание паттернов проектирование". Вобщем много чего он мне наговорил. Книжек умных насоветовал, подкинул MSVS 2008, кучу электронных книжек. Я изучил C# по книжке Шилдта. Читал "Идеальный кол" (автора уже не помню). Потом купил(!) себе книжку Шилдта про С++. Мне понравился язык. Тем более что мне казалось, что именно он и есть общепринятый стандарт. Наиболее удобный язык для программиста.

А недавно в соседней теме за упоминание это С++ меня чуть было не съели со всем чем можно. Так-то.

Собственно вопрос: Так стоит ли изучать дальше С++ (а я уже достаточно углубился в книжку Страуструпа, подробно изучая все подводные течения)? Какой язык стоит изучать? Какие из них более востребованны?

Спасибо всем, кто осилил это многобукаф.

★★★★★
Ответ на: комментарий от www_linux_org_ru

>откуда дровишки, особенно насчет for(i=0; i<3; i++) ?

Я изучал листинги кода который рожает intel c++ когда ему кормишь примеры из книги с велосипедами.

>частично применять функции должен компилятор (ну или суперкомпилятор), делать же это вместо него руками (и макросами) -- это мазохизм и источник багов.

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

Absurd ★★★
()
Ответ на: комментарий от www_linux_org_ru

>Потом оба поумнели.

умный человек STL на C++ не напишет

ну вот, например, простая как валенок задача - переопределить operator<< (для работы со стандартным же ostream) для вектора векторов при условии, что operator<< для вектора произвольного типа мы уже задали. идиоматически предполагается использование стандартного алгоритма copy (все подряд, включая Степанова, хором рекомендуют использовать именно алгоритмы STL вместо явных циклов)

простая вроде задача, использующая полную идиоматику STL и стандартной библиотеки C++ в принципе. покажешь красивое решение, которое бы компилировалось и работало, и использовало бы только стандартные средства (без boost)?

про аналогичное определение operator>> (для istream) тоже можно было бы рассказать - как хорошо там можно воспользоваться стандартными алгоритмами, и насколько это будет лучше сишного fscanf'а

jtootf ★★★★★
()
Ответ на: комментарий от Absurd

> Я изучал листинги кода который рожает intel c++ когда ему кормишь примеры из книги с велосипедами.

Ну так поделись минимальной версией, где icc (хотя лучше gcc) отказывается разворачивать циклы.

> Когда С++ упрекают в низкой грануляроности контроля над тем во что разворачивается код объявлется багоопасность, а когда С++ упрекают в отсутствии высокоуровневых примитивов объявляется низкая гранулярность контроля.

Нужно и то, и другое -- и гранулярность, и абстракции. Примеры, где этого не хватает -- в студию.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

>вот у меня основная притензия к языкам, претендущим на высокоуровнеовсть (допустим, яве и хаскелю), в том, что они не дают возможности создать самому реализацию моих абстракций тогд, когда это для меня критично

с этим - согласен, но решаться это должно, как по мне, не на уровне самого языка - а на уровне реализации. ну или явного описания диалекта, как у REBOL (таким же образом можно явно выписать правила оптимизации для модуля - что-то подобное есть у GHC - и, возможно, даже декларативно описывать кодогенератор или его часть)

>в некторых случаях хотелось бы сделать свою реализацию

например?

jtootf ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

>> Я изучал листинги кода который рожает intel c++ когда ему кормишь примеры из книги с велосипедами.

>Ну так поделись минимальной версией, где icc (хотя лучше gcc) отказывается разворачивать циклы.

Перемножение матриц 4x4 - icc разворачивет внутренний цикл по i и не разворачивает внешний по j. Выражения вида a*b*c разворачиваются в три call-а, т.к функции с циклами не инлайнятся.

Absurd ★★★
()
Ответ на: комментарий от jtootf

>> в некторых случаях хотелось бы сделать свою реализацию

> например?

видимо это мне стоит запостить отдельным тредом в девелопмент, т.к. здесь модераторы уже давно анонимуса отключили, и уже начали ограничения по звездам вводить, не дай ТНБ, к концу флейма я уже право голоса потеряю :-)

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

>стоит запостить отдельным тредом в девелопмент

да, неплохая мысль

>здесь модераторы уже давно анонимуса отключили, и уже начали ограничения по звездам вводить

непорядок :(

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

> ну или явного описания диалекта, как у REBOL

диалект тут м.б. не в кассу, т.к. речь идет об оптимизации *реализации*, вполне возможно в рамках стандартного синтаксиса и даже семантики (хотя...?)

> таким же образом можно явно выписать правила оптимизации для модуля - что-то подобное есть у GHC

так дай ссылку

что касается твоей задачи, не вижу где подвох -- все вроде просто, завтра пробую

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от Absurd

> Выражения вида a*b*c разворачиваются в три call-а, т.к функции с циклами не инлайнятся.

у меня щас жцц древний 3.4.4 под цигвин вроде как развернул одно матричное умножение без колл-ов, завтра посмотрю

и вообще мне сдается, что "функции с циклами не инлайнятся" -- это из подлянок борланда 15-летней давности

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от jtootf

переопределить operator<< (для работы со стандартным же ostream) для вектора векторов при условии, что operator<< для вектора произвольного типа мы уже задали

В чём проблема?

#include <stdlib.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

// этот велосипедик в ожидании нативных лямбд в gcc 4.5
template<class T> class print : public std::unary_function<T, void>
{
  std::ostream& os;
  char const * const m_delim;
public:
  print(std::ostream& out, const char * delim) : os(out), m_delim(delim) {}
  void operator() (T x) { os << x << m_delim; }
};

template <class T>
std::ostream & operator<<(std::ostream & os, const std::vector<T> & vector)
{
  std::for_each(vector.cbegin(),vector.cend(), print< T >(os, " "));
  return os;
}

template <class T>
std::ostream & operator<<(std::ostream & os, const std::vector< std::vector<T> > & vector)
{
  std::for_each(vector.cbegin(),vector.cend(), print< std::vector<T> >(os, "\n"));
  return os;
}

int main()
{
  std::vector<  std::vector<int>> veve;
  veve.resize(3);
  for (auto ve=veve.begin();ve!=veve.end();++ve)
  {
    ve->resize(100);
    std::generate(ve->begin(), ve->end(), random);
  }
  cout << veve;
}
legolegs ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

>у меня щас жцц древний 3.4.4 под цигвин вроде как развернул одно матричное умножение без колл-ов, завтра посмотрю

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

>и вообще мне сдается, что "функции с циклами не инлайнятся" -- это из подлянок борланда 15-летней давности

Нет, это из полдянок компилятора который понимает буст и локи.

Absurd ★★★
()
Ответ на: комментарий от legolegs

>В чём проблема?

во введении вспомогательного класса. почему это нельзя сделать через std::copy и итераторы (т.е. почему в стандартный алгоритм можно передать пользовательский функтор, но нельзя воспользоваться расширением стандартной библиотеки)?

jtootf ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

>диалект тут м.б. не в кассу

я имел в виду тот же принцип. речь идёт, естественно, о неизменном синтаксисе/семантике языка

>так дай ссылку

http://www.haskell.org/ghc/docs/6.10-latest/html/users_guide/pragmas.html

http://www.haskell.org/ghc/docs/latest/html/users_guide/rewrite-rules.html

типа вот такого

jtootf ★★★★★
()
Ответ на: комментарий от legolegs

вопрос снят, я идиот. хотя решение для разрешения имён всё равно неочевидное и мне не нравится

jtootf ★★★★★
()
Ответ на: комментарий от legolegs

продолжаем играться с чудесным языком C++. как этот код распространить на статически определённые массивы? я в некотром замешательстве

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

как этот код распространить на статически определённые массивы?

В смысле на int arr[100500]; ?

Тут, хотя компилятору и известен размер массива, нет способа передать этот размер в operator<< привычным образом. Можно использовать статические массивы из буста, они поддерживают итераторы.

Если бы концепты были допилены в новом стандарте, была бы (наверно) возможна конструкция типа такой:

template <class T>
std::ostream & operator<<(std::ostream & os, const sequence_container<T> & vector)
{
  std::for_each(vector.cbegin(),vector.cend(), print< T >(os, " "));
  return os;
}

Она бы работала и с std::vector и std::list и boost::array.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

>В смысле на int arr[100500]; ?

да

>Можно использовать статические массивы из буста

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

jtootf ★★★★★
()
Ответ на: комментарий от legolegs

Накропал по-быстрому:

#include <stdlib.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <boost/array.hpp>
typedef long TYPE;

template<class T> class print : public std::unary_function<T, void>
{
  std::ostream& os;
  char const * const m_delim;
public:
  print(std::ostream& out, const char * delim) : os(out), m_delim(delim) {}
  void operator() (T x) { os << x << m_delim; }
};

//общая выводилка контейнеров
template <class T>
    void arr_out(std::ostream & os, const T & vector, const char * delim)
{
  std::for_each(vector.begin(),vector.end(), print< typename T::value_type >(os, delim));
}

//копипаст на каждый контейнер :(
template <class T>
inline std::ostream & operator<<(std::ostream & os, const std::vector<T> & vector)
{ arr_out(os, vector, " "); return os; }

template <class T, std::size_t N>
inline std::ostream & operator<<(std::ostream & os, const boost::array<T, N> & vector)
{ arr_out(os, vector, " "); return os; }

//особый случай
template <class T>
    std::ostream & operator<<(std::ostream & os, const std::vector< std::vector<T> > & vector)
{ arr_out(os, vector, "\n"); return os; }
template <class T, std::size_t N1, std::size_t N2>
    std::ostream & operator<<(std::ostream & os, const boost::array< boost::array<T, N1> , N2> & vector)
{ arr_out(os, vector, "\n"); return os; }

template <class T>
void arrout(T & arr)
{
  for (auto ve=arr.begin();ve!=arr.end();++ve)
  {
    std::generate(ve->begin(), ve->end(), random);
  }
  std::cout << arr;
}

int main()
{
  std::vector<  std::vector<TYPE>> veve(3);
  for (auto ve=veve.begin();ve!=veve.end();++ve)
    ve->resize(100);
  arrout (veve);
  
  std::cout << std::endl;
  boost::array< boost::array<TYPE,100>,3> boostar;
  arrout (boostar);
  

  return 0;
}
legolegs ★★★★★
()
Ответ на: комментарий от jtootf

Сишные массивы по традиции приводятся к указателю на первый элемент. Невозможно напрямую передать их в функцию.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

Сишные массивы по традиции приводятся к указателю на первый элемент. Невозможно напрямую передать их в функцию.

в функцию - да, а вот в шаблон функции, параметризованный размером массива - можно

template <typename T, size_t n>
size_t size(T (&)[n])
{
   return n;
}
jtootf ★★★★★
()
Ответ на: комментарий от legolegs

>Накропал по-быстрому:

копипасту как раз побороть можно, но вот использование boost.array расстраивает. то есть, обобщение ещё и на boost.array - дело нормальное, но отказываться от встроенных массивов (при том, что указатель удовлетворяет концепту итератора) не хочется

jtootf ★★★★★
()
Ответ на: комментарий от legolegs

Найден способ борьбы с сишным наследством: другое сишное наследство.

template <class T, std::size_t N>
inline std::ostream & operator<< ( std::ostream & os, const T ( *vector ) [N] )
{
  os << __PRETTY_FUNCTION__ << std::endl;
  std::for_each (  *(vector) ,*(vector)+N , print< T > ( os, " " ) );
  return os;
}

int main()
{
  int foo[100];
  std::generate ( foo, foo + 100, random );
  std::cout << &foo;
}
legolegs ★★★★★
()
Ответ на: комментарий от jtootf

>копипасту как раз побороть можно

Что-то не соображу как.

>но отказываться от встроенных массивов

Уже можно не отказываться, но передавать по указателю (можно по ссылке, как в примере с size, то для operator<< это ломает вывод строк.

>при том, что указатель удовлетворяет концепту итератора

Для итераторов надо иметь begin и end, в operator<< их просто непонятно куда передавать.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

>Найден способ борьбы с сишным наследством: другое сишное наследство.

неплохо, да. спасибо

jtootf ★★★★★
()
Ответ на: комментарий от Karapuz

>проходят годы, но всё так же велика доля чудаков

количество разума на земле - величина постоянная

jtootf ★★★★★
()
Ответ на: комментарий от Karapuz

однако сошлись же на Tcl/Tk, что оставляет веру в светлое будущее человечества :)

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

>>проходят годы, но всё так же велика доля чудаков

>количество разума на земле - величина постоянная

Именно поэтому множится число людей, боящихся всяких "утечек памяти" и прочих указателей.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

>Именно поэтому множится число людей, боящихся всяких "утечек памяти" и прочих указателей.

Всё больше и больше народу пишет на С(++)? Странно... :\ Остальных это волновать не должно - не? =)

yyk ★★★★★
()
Ответ на: комментарий от legolegs

>Именно поэтому множится число людей, боящихся всяких "утечек памяти" и прочих указателей.

Я уже раза три в этом треде озвучивал альтернативы:

а) Мы делаем язык для прикладных задач и реализуем GC.
б) Мы делаем язык для системных/RT задач и реализуем ручное управление динамической памятью для получения гарантированного отклика.

В ++ не сделано ни того ни того.

Absurd ★★★
()
Ответ на: комментарий от Absurd

>Мы делаем язык для прикладных задач и реализуем GC

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

>Мы делаем язык для системных/RT задач и реализуем ручное управление динамической памятью для получения гарантированного отклика.

Хаха. Вообще-то в гуе гарантированный отклик в сто раз нужнее, чем в каком-нибудь bind.

>В ++ не сделано ни того ни того.

В би++ - может быть, а в си++ есть и то и то.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

>Тут уже неоднократно говорилось, что GC решает наименьшую проблему, оставляя кучу других.

Вранье. Управление памятью это 99 процентов всех проблем. Проблемы незакрытого сокета просто не существует по сравнению с этим.

>К сборщику мусора нужен автозакрывальщик дескрипторов, автооткатыватель транзакций и вообще автообъявлятель переменных, иначе мы наблюдаем профанацию.

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

>>Мы делаем язык для системных/RT задач и реализуем ручное управление динамической памятью для получения гарантированного отклика.

>Хаха. Вообще-то в гуе гарантированный отклик в сто раз нужнее, чем в каком-нибудь bind.

В MS-овских гуйках статус контролов (задизейблен-отмечен галочкой - дефлотный-итп) обновляется в обработчике WM_IDLE. Ты видишь какой-нибудь лаг? А по меркам проца происходят бездны времени между изменением модели, констатацией состояния программы как "IDLE", посылкой WM_IDLE и установлением нового статуса контрола на основании модели.

>>В ++ не сделано ни того ни того.

>В би++ - может быть, а в си++ есть и то и то.

Ложь - gc нет, управления памятью в RT-стиле тоже нет.

Absurd ★★★
()
Ответ на: комментарий от Absurd

>> В би++ - может быть, а в си++ есть и то и то.

> Ложь - gc нет, управления памятью в RT-стиле тоже нет.

А что такое "управление памятью в RT-стиле"?

tailgunner ★★★★★
()
Ответ на: комментарий от Absurd

>Вранье. Управление памятью это 99 процентов всех проблем.

Неужели ты хочешь сказать, что нулевые указатели, race conditions, deadlocks, перепутанные переменные, опечатки в строках, неотфильтрованный юзерский ввод, забытые проверки условий, округления, ошибки представления float, кретинские предположения и ещё кучи всевозможных программёрских косяков - это 1%? Ха, да кого вообще волнует эта память? На современных машинах её вообще можно блин не освобождать.

>Проблемы незакрытого сокета просто не существует по сравнению с этим.

А нука представь себе веб-сервер, не закрывающий сокет.

>Закрытие дескрипторов всегда находится на одном уровне с открытием

Конечно, точно так же, как и выделение памяти. Wait. Oh shi~

>Ну с простыми исключениями типа фабрик

В простых случаях фабрики можно и не использовать.

>Ты видишь какой-нибудь лаг?

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

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

> На современных машинах её вообще можно блин не освобождать.

Оно и видно по качеству современных поделий... Чё там, утечек не бывает, память не фрагментируется и ваще её можно не освобождать! (с)

kemm
()
Ответ на: комментарий от kemm

>Чё там, утечек не бывает, память не фрагментируется и ваще её можно не освобождать! (с)

Ты был грустный сарказм вообще-то.

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

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

Да-да, если изредка случаются аварии, где ни ремень, ни подушка безопасности не помогают, значит, рмень и подушка -- говно. Логика железная. То, что спасают в абсолютном большинстве случаев, никогда не волнует, ога.

kemm
()
Ответ на: комментарий от kemm

Речь не о подушке и ремне. gc - это как если бы каждого пассажира перевозили в отдельной 40тонной цистерне с желе. При этом удары с боков и сзади по прежнему смертельны.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

> Речь не о подушке и ремне. gc - это как если бы каждого пассажира перевозили в отдельной 40тонной цистерне с желе. При этом удары с боков и сзади по прежнему смертельны.

Красивая аналогия. Доказательства её применимости будут?

kemm
()
Ответ на: комментарий от kemm

> Красивая аналогия. Доказательства её применимости будут?

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

tailgunner ★★★★★
()
Ответ на: комментарий от legolegs

> нулевые указатели

Нулевые указатели, ИМХО, зло №2 после ручного управления памяти. Кстати, в Хаскелле их нет, там Maybe рулит неподецки.

pitekantrop ★★★
()
Ответ на: комментарий от pitekantrop

> Аналогии вообще не могут служить доказательствами.

Наш толстый бесполезный тролль сформулировал правильно: "доказывать применимость аналогий". _Применимость_. Применимость своих аналогий он доказывать не стал - тролль же.

tailgunner ★★★★★
()
Ответ на: комментарий от legolegs

>Неужели ты хочешь сказать, что нулевые указатели, race conditions, deadlocks, перепутанные переменные, опечатки в строках, неотфильтрованный юзерский ввод, забытые проверки условий, округления, ошибки представления float, кретинские предположения и ещё кучи всевозможных программёрских косяков - это 1%?

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

>>Проблемы незакрытого сокета просто не существует по сравнению с этим.

>А нука представь себе веб-сервер, не закрывающий сокет.

Ошибки такого рода не доходят даже до бета-тестеров. Ну с незакрытой транзакцией - придет на следующий день базовик с кучей WTF-сов.

>>Закрытие дескрипторов всегда находится на одном уровне с открытием

>Конечно, точно так же, как и выделение памяти.

Это сознательная ложь: объекты надо постоянно передавать вовне, в чужой код.

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

Он AST-дерево C++- проекта наверно перестраивает

Absurd ★★★
()
Ответ на: комментарий от pitekantrop

В Scala есть Option -- аналог Maybe -- жутко удобен для тех мест, где мог бы быть null. Только, к сожалению, там нет MaybeT (монадный трансформер).

pitekantrop ★★★
()
Ответ на: комментарий от legolegs

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

Приложения на С++ в своп не уходят?

pitekantrop ★★★
()
Ответ на: комментарий от tailgunner

>> Ложь - gc нет, управления памятью в RT-стиле тоже нет.

>А что такое "управление памятью в RT-стиле"?

Работа с пулами. В С++ ее просто нет. На возможное плюсофильское вякание по поводу перегруженных new/delete есть замечательная цитата Изи Крейнина выше по треду.

Absurd ★★★
()
Ответ на: комментарий от legolegs

>Кто-то рассказывал, как в проекте на няшной жабке (а может шарпе) забывали удалять объекты из глобального массива.

Это ошибка уже другого рода которая решается чисто механистически - не допускать неограниченно растущих коллекций в синглтонах.

Absurd ★★★
()
Ответ на: комментарий от tailgunner

> А ты применимость своих аналогий доказывать собираешься?

А что, АО не очевидно, что GC спасает от утечек памяти (подушка и ремень спасают при столкновениях до N км/ч), и не спасают от логических утечек (от столкновения на 2N км/ч)? Что классические утечки случаются чаще, чем логические? Что у тебя вызывает сомнения?

kemm
()
Ответ на: комментарий от tailgunner

> Наш толстый бесполезный тролль сформулировал правильно: "доказывать применимость аналогий". _Применимость_. Применимость своих аналогий он доказывать не стал - тролль же.

Не лопни, окружающих забрызгаешь.

kemm
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.