241,63
рейтинг
25 июня 2014 в 10:59

Разработка → Почему Ваза утонул, а С++ всё ещё на плаву tutorial

Эта статья — краткий пересказ невероятно интересного доклада Скотта Майерса для тех, у кого нет 70 минут на весь доклад, но есть 7 минут на основные тезисы.

Некоторые люди, которые не пишут на С++, а лишь слышали об этом языке, задаются вопросом: «Почему вообще кто-то пишет на C++?». Но есть люди, которые используют С++ каждый день, и вот эти люди задаются вопросом: «А действительно, почему я пишу на этом языке?».

Но ведь действительно, должна быть какая-то причина, по которой люди пишут программы на С++. Давайте вернемся в начало 90-ых, когда проходила стандартизация С++. Была предложена масса идей. Предложений было столько и они были настолько разные, что мне запомнилась цитата Джима Вальдо, который тогда работал в комитете по стандартизации: «Каждый, предлагающий добавить что-то в С++ должен приложить к заявке свою почку. Тогда никто не предложит больше двух идей, а к выбору этих двух он подойдёт невероятно ответственно.»

Язык, который был бы получен в результате принятия всех предложений, выходил слишком сложным и тогда Бьёрн Страуструп сказал «А помните Ваза?». Никто, кроме людей из Швеции, не понял о чём речь. Ваза был огромным боевым кораблём, построенным в Швеции в 1625 году. Основным принципом постройки корабля было «А почему бы нам не добавить сюда ещё и вот такую фичу?». Многие из идей исходили непосредственно от короля, в частности он лично утверждал размеры корабля. Также на Ваза по указаниям свыше требовалось нацепить огромное количество элементов украшения, резьбы, большое количество пушек и т.д. А королю ведь не откажешь. Итог был закономерным — из-за ошибок в конструировании Ваза затонул в первом же рейсе, едва выйдя из бухты.

Давайте посмотрим на С++. Является ли он настолько же «нагруженным» фичами? На первый взгляд — без сомнения:



Давайте посмотрим вот на это:
f(x);


Вроде бы всё ясно — это функция f, которой передаётся х. Так? Не факт. Это может быть функция, а может быть указатель на функцию, а может быть ссылка на функцию. Ещё это может быть объект, в классе которого перегружен оператор скобок. Или объект, который неявно может быть сконвертирован в что-то из вышеуказанного. Или одна из перегруженных функций. Или шаблон. Или несколько из вышеуказанных вещей одновременно.

Я начинаю понимать, почему люди считают С++ сложным. Кажется, что всё вышеуказанное спроектировано совершенно неверно. Слишком сложно.
Но подождите. Давайте измерим пару вещей. Например, количество вакансий для программистов на разных языках, количество кода на них в открытых источниках, количество поисковых запросов к поисковикам и т.д. И если мы посмотрим на статистику, в течении последних 25 лет С++ находится на 2-4 месте по популярности в мире.



С++ на втором месте по количеству кода в opensource (12%) и этот процент растёт год от года.



Соответственно, что-то в этом языке сделано правильно. Почему-то он плывёт. Давайте разбираться почему.

Совместимость с С

Мы можем взять код на С — и использовать его в С++. Мы можем взять редактор, IDE, линкер, дебаггер от С — и использовать их для С++ сразу или с минимальными изменениями. Мы можем взять программиста на С, сказать ему «ты уже знаешь часть фич С++, посмотри на ООП, STL и ещё пару вещей — и ты сможешь писать на С++». Эта вещь была невероятно важной в 90-ых и начале 2000-ых. Да и сегодня код на С — самый распространённый в мире.

Самая важная фича языка

Что бы вы назвали самой важной фичей языка? (Реплики из зала: «фигурные скобки», «шаблоны»). Нет. Это деструкторы. Деструкторы сделали возможным RAII. Деструкторы упростили сложность используемых в программировании концепций на порядок. В деструкторе мы можем освободить память, мьютекс, закрыть файл или сетевое соединение. Деструкторы спасают нас от веток условий, от ненужных goto.

Шаблоны

На Марсе есть небольшой такой кратер, который получился от того, что в эту планету бодро врезался сделанный на Земле исследовательский аппарат, который вообще-то разбиваться был не должен. Но разбился. Разбился потому, что в одном из кусков его кода программист решил использовать метрическую систему мер, а в другом куске кода, другой программист — классическую английскую. И вот как-то оказалось, что килограмм не равен фунту.
Шаблоны в С++ дали нам возможность писать абстрактный код, алгоритмы, функции и классы, которые на этапе компиляции могут надёжно и однозначно быть специфицированы какими-то определёнными типами. Это огромный шаг вперёд. И никаких затрат на рантайме!

Перегрузка

Мы можем перегрузить функции, методы, операторы. Мы можем сделать код проще и выразительнее. Перегрузка оператора скобок стала основой для лямбд в стандарте С++11, а лямбды очень полезны.

Язык для библиотек

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

Мультипарадигменность

Процедурное программирование? Ок. Объектно-ориентированное программирование? Ок. Метапрограммирование на шаблонах? Ок. «Опасное программирование» с указателями и ассемблерными вставками? Ок. Смешанные концепции? Тоже ок. Даже функциональное программирование в какой-то мере уже становится возможным с приходом последних стандартов С++.

Лучший язык для «программирования с препятствиями»

Возможно, С++ не лучший язык всех времён и народов, но на данный момент он лучший язык для того, что я называю «программированием с препятствиями». Препятствием может быть неизвестная предметная область, высокие требования по быстродействию или потребляемой памяти, необходимость общаться со странным чужим кодом или аппаратными устройствами, неопределённость задач и т.д. Выбрав С++ в ситуации с наличием вышеуказанных (или других) препятствий — у вас самые высокие шансы не запороть проект в какой-то момент просто потому, что на выбранном языке решить задачу невозможно.

Вы не платите за то, что не используете

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

«Платформенно-независимый ассемблер»

Еще одно свойство совместимости с С. Программа на С++ не так далеко ушла от ассемблера. В большинстве случаев можно понять в какой набор ассемблерных инструкций развернётся код на С++. Как результат — можно писать эффективные программы под любые платформы. Более того, под некоторые платформы только на С/С++ писать и можно.

Обратная совместимость

Код на С++, написанный в начале 90-ых на первых версиях стандарта С++ можно просто взять и скомпилировать современным компилятором, поддерживающим С++14. И он скомпилируется. Ну, с большой вероятностью. И будет делать то, что должен. Опять таки, с очень большой вероятностью. Есть пару мест, которые могут сломаться, но их мало и они общеизвестны. Таким образом мы говорим компании: инвестируя сегодня в код на С++ вы можете быть спокойны — и через 10 и через 20 лет ваш код будет работать, а значит ваши деньги не пропадут.

Сложность, которую можно спрятать

Языку С++ часто пеняют тем, что код на нём — сложный. Вернее было бы сказать, что на С++ действительно можно (при желании) написать сложный код. И ещё о коде на С++ можно сложно говорить. Давайте взглянем на вот это:

std::cout << x;


О Боже мой! Да здесь же есть пространства имён, потоки, шаблон (неявно специфицированный), объект класса (унаследованного от другого класса) и перегрузка операторов. Кошмар! Но взгляните на код выше ещё раз. Вы правда не понимаете, что там написано? Вы правда задумываетесь о всех этих страшных терминах, которые я упоминал, когда читаете этот код? Нет. Сложность прячется от вас, вы её не замечаете.

Язык для тех, у кого нет другого выбора

Люди, которые решают писать на С++, живут не в танке. Зачастую они знают парочку других языков — скриптовых, управляемых, функциональных. Там, где можно сэкономить себе время и силы — они на них и пишут. Но иногда оказывается, что выбора нет — проблема слишком сложна, требования слишком высоки, риски запредельны. И тогда на сцену выходит С++. Сложный, но мощный инструмент для сложных, но масштабных задач. Это, к стати, одна из причин, почему программирование на С++ и код на С++ считаются сложными — средний уровень решаемых на этом языке задач является более высоким. Наивно ждать решения сверх-сложной задачи простым минималистичным кодом.

Язык для тех, кто любит сложности

Да, на С++ очень просто выстрелить себе в ногу. Даже множеством разных способов. Но для некоторых людей это плюс. Возможность выстрелить себе в ногу предполагает возможность стрелять вообще куда-угодно, как-угодно и сколько-угодно. Да, результаты могут быть ужасными. Но и мощь устрашающа.

Стандарт становится больше и… проще

Нам говорят, что в стандарте языка С++ в 1990-ом году было 400 страниц, в 1998 — 700, а в 2011 стало 1300. «Язык становится сложнее» — нам говорят. Нет, не становится. Да, мы вводим новые понятия и фичи, но большинство их них призваны не усложнить, а упростить код. Вспомните, как вам приходилось бегать итераторами по векторам — и вот в С++11 у нас есть range-based for и auto. Мы можем написать одну строку кода там, где раньше было 5. Лямбда-функции дали возможность исключить из кода целые сущности — лишние классы и методы, которые по факту были не нужны. Да, разработчикам стандарта приходится обдумывать и писать существенно больше текста. Но конечному программисту становится только легче.
Автор: @tangro

Комментарии (261)

  • +16
    std::cout << x;
    

    Если взглянуть на эту строчку глазами человека, который вообще ни разу не имел дела с C++, то она совершенно непонятна. Максимум, что можно сказать — что-то делается с переменной x. Или это не переменная? О значении идентификатора cout и сдвига без знания конвенции не догадаешься — хотя согласен, что привыкаешь к ней довольно быстро.
    • +26
      Если некоторые люди взглянут на некоторые строчки некоторых языков, с которыми они никогда не имели дела, то они могут показаться им совершенно непонятными. И это относится к любому языку программирования, всегда найдутся конструкции, в которых без детального изучения не разобраться.
    • +2
      echo explode(":", "foo:*:1023:1000::/home/foo:/bin/sh");
      

      Регэкспы ещё можно вспомнить, вот уж где точно
      без знания конвенции не догадаешься
      \b(?:\d{1,3}\.){3}\d{1,3}\b
      • +5
        Регекспы — отличный пример write-only кода. Но лучше пока никто ничего не придумал (вербальные выражения не в счет).
        • +12
          А по-моему, write-only код — это код людей, которые не зная reg exp'ов фичачат вместо одного паттерна 35 strpos.
          • +4
            Вспомнилось
            bool IsBukva(char symbol)
            {
            switch(symbol)
            {
            case'a':return 1;break;
            case'b':return 1;break;
            case'c':return 1;break;
            case'd':return 1;break;
            case'e':return 1;break;
            case'f':return 1;break;
            case'g':return 1;break;
            case'h':return 1;break;
            case'i':return 1;break;
            case'j':return 1;break;
            case'k':return 1;break;
            case'l':return 1;break;
            case'm':return 1;break;
            case'n':return 1;break;
            case'o':return 1;break;
            case'p':return 1;break;
            case'q':return 1;break;
            case'r':return 1;break;
            case's':return 1;break;
            case't':return 1;break;
            case'u':return 1;break;
            case'v':return 1;break;
            case'w':return 1;break;
            case'x':return 1;break;
            case'y':return 1;break;
            case'z':return 1;break;
            case'A':return 1;break;
            case'B':return 1;break;
            case'C':return 1;break;
            case'D':return 1;break;
            case'E':return 1;break;
            case'F':return 1;break;
            case'G':return 1;break;
            case'H':return 1;break;
            case'I':return 1;break;
            case'J':return 1;break;
            case'K':return 1;break;
            case'L':return 1;break;
            case'M':return 1;break;
            case'N':return 1;break;
            case'O':return 1;break;
            case'P':return 1;break;
            case'Q':return 1;break;
            case'R':return 1;break;
            case'S':return 1;break;
            case'T':return 1;break;
            case'U':return 1;break;
            case'V':return 1;break;
            case'W':return 1;break;
            case'X':return 1;break;
            case'Y':return 1;break;
            case'Z':return 1;break;
            default:return 0;
            }
            }
            
            • +2
              Дададада! Я как-то давно (ОЧЕНЬ ДАВНО) читал код чудо-движка DLE, тогда он был открыт и бесплатен. Так вот, там 50% (очевидно старого кода) было написано с тыщам strpos, а потом автор, очевидно, выучил рег экспы и в более новом коде появились кривые паттерны в стиле \S*, но это уже всяко был прогресс против strpos-то =)
              • +2
                Стоит отметить, что в PHP иной раз замена через regexp может быть в 30 раз медленнее замены через str_replace, поэтому шутки шутками, но без бенчмарков судить сложно.
                • +2
                  Ну если посмотреть на то, как сделаны regexp'ы в PHP, то можно придти к выводу, что язык явно писали садисты. Ибо работа с regexp'ами состоит из двух частей:
                  1) Компиляции regexp'ов (очень медленно и требует кучу ресурсов)
                  2) Использование regexp'ов (быстро и просто)
                  Что делают нормальные люди? Разделяют эти стадии явно (C/C++, Java, etc), либо неявно (JavaScript, Perl, etc). Что делают разработчики PHP? Объединяют их на уровне API и гарантируют, что это это мощное, быстрое и лёгкое средство будет жрать ресурсы как не в себя.
                  • 0
                    Не могу вспомнить, чтобы в результатах профилирования на реальных проектах у меня preg_* функции показывали какой-либо заметный процент.

                    Вот сейчас как раз открыт результат профилирования. preg_match вызывается 6494 раз. Суммарное время выполнения — 14 мс (это на 2 секунды работы скрипта). preg_replace вызывается 828 раз. 1.7 мс.

                    Для сравнения по встроенным функциям:
                    Функция — число вызовов — суммарное время, мс
                    mysql_query — 54 --123
                    fclose — 4 — 44
                    file_put_contents — 297 — 26
                    file_get_contents — 55 — 16

                    Вы точно не путаете с древними ereg_* — вот те были реально тормозными.



                    Нашёл в логах пример потяжелее, там шла конвертация аж на 42 секунды длительностью. Там:
                    preg_match — 45883 раз — 28мс
                    preg_replace_callback — 9080 раз — 156мс
                    preg_replace — 658 раз — 1.4 мс
                    • +1
                      Вы путаете «кисло с пресно» — операции с БД и файловые операции всегда очень дорогие. Заведомо дороже регэкспов, которые только в памяти. Есть ли операции не связанные с этими сущностями, но отнимающие сравнимое количество времени?
                      • +1
                        Я к тому, что «преждевременная оптимизация — зло» © Да, существуют задачи, в которых скорость работы со строками критична. Но PHP тут окажется не лучшим выбором хоть с preg-функциями, хоть со str. А на тех задачах, где этот язык востребован, скорость работы preg оказывается совершенно пренебрежимым фактором.
                        • 0
                          Вообще я так подозреваю, что работа с файлами — не основная специализация PHP. Или я отстал от жизни? А если вычесть файловые операции, то доля регулярок достаточно значимо возрастёт. Впрочем без полных данных профилирования сложно что-то говорить. И в любом случае — изначально скорость регулярок сравнивалась со скоростью прямого манипулирования со строками.
                          • 0
                            Да при чём тут файловые операции. Я Вам про время работы регекспов относительно типичного времени работы скрипта. И, если уже про основную специализацию, то Вы очень вольно пропустили mysql_query. Если выкинуть из этого примера файловые операции, доля регулярок вырастет, но всё равно составит пренебрежимую величину.

                            И в любом случае — изначально скорость регулярок сравнивалась со скоростью прямого манипулирования со строками.


                            Это где так было? Я такого не видел ни в одном языке. В том числе в Си (как пример «быстрого языка»), и в Perl'е (как примере языка, для которого регулярки — основа).
                            • 0
                              «Типичного» времени работы скрипта? Что же это за скрипт, который целых две секунды работает?

                              Это где так было? Я такого не видел ни в одном языке. В том числе в Си (как пример «быстрого языка»), и в Perl'е (как примере языка, для которого регулярки — основа).

                              Это было в этой ветке комментариев.
                  • 0
                    Я бы так не сказал. Написав добрых несколько дюжин веб пауков и парсеров, для себя сделал вывод, что где нет простых каких-то замен, то regexp явно в выигрыше. Один регексп лучше десяти str_replace или strpos + substr.
                • 0
                  … и не только в PHP (те же boost regexp и pcrecpp в n раз медленнее операций со строками).
      • +2
        \b(?:\d{1,3}\.){3}\d{1,3}\b

        Ни на одном языке не пишут в одну строчку, «доказывая» нечитаемость кода, а на регулярках — всегда пожалуйста. Код форматировать надо.

        \b
        (
            \d{1,3}
            \.
        ){3}
        \d{1,3}
        \b

        И внезапно регулярка уже читается, причём безо всяких извращений вроде «verbal expressions». Здесь достаточно помнить значение «b» и «d» (множители и скобки забыть сложно).

        Этот режим (ignore whitespace + explicit capture) поддерживается всеми нормальными библиотеками, поэтому, если кто-то продолжает писать write-only код, то он сам себе злобный Буратино.
        • +12
          помоему стало только хуже.
          • 0
            Вы серьёзно? Неужели вы так сломали себе мозг, что код написанный в строку, для вас понятнее, чем табулированный и разобранный? Я бы еще комментарии добавил. Когда мне приходилось в Java работать с регулярными выражениями, я их так и писал. И читать регексп любой сложности — пустяшное дело.
            • 0
              Вы их настолько разбиваете? Такие короткие регекспы обычно оставляют одной строкой.
              • 0
                Да просто пример неудачный. Когда регекспа больше в 3-4 раза и можно натыкать внутрь комментариев, тогда да, она становится читаемой.
                • 0
                  Regexp становится читаемым, если смотреть на него в каком-нибудь визуальном тестере, типа вот этого: www.debuggex.com/

                  А так всё это форматирование, комментарии для накрученных регекспов — это как мёртвому припарки (утрировано говоря).
              • 0
                Это — вопрос уровня квалификации и понимания читающего. Я в регекспах довольно слаб. Хотя, конечно, думаю, бил бы чуть крупнее. Но я понял заявление в духе «в строчку было бы лучше» и только.
    • +13
      Взглянем на строчку из английского языка, никогда его не учив. Максимум что понятно, это то, что это надо как-то произнести…
      • –1
        Ваша аналогия не совсем корректна. Представьте ситуацию: вы показываете человеку, который не знает английского и далек от программирования, распечатку кода на, прости господи, языке 1C. Даже в таком случае он уловит какую-то часть логики, потому что она выражается через общеупотребительные слова. А вот догадаться без мануала, что делают функции с названиями вроде gcvt или wcstombs не сможет даже опытный программист, свободно говорящий по-английски.
        • +4
          Нет, верна, вы выбрали наиболее специфичный синтаксис и требуете от него кристальной понятности, для человека которой ни с плюсами, ни с си, ни вообще с *nix не работал.
          Конструкции if while, вызова функций ну т.д. в плюсах теже, что и в других языках и общая понимаемость вполне себе.

          P.S На закуску, не из плюсов: () => {x+=1; y+=1;}
          • –3
            Любой шарпист поймет это без всякой помощи
            • +2
              Ну да из них, а кроме кто-то поймет?
              • 0
                Лямбда?
                P.S. не шарпист
                • 0
                  Да, она самая. Наверное стоило написать что-то типа того

                  () => x+=1;
          • 0
            (x,y) => {x+=1; y+=1;}
            • 0
              Хм, а вот так, на мой взгляд, даже для непосвященного человека будет понятно:
              Самое сложное в программировании — это называть переменные понятными именами

              var increaseXandYandPrintResult = new Action<int, int>((x, y) =>
              {
              x += 1;
              y += 1;
              Console.WriteLine(x);
              Console.WriteLine(y);
              });
              • 0
                Ну коль пошло в статье и стартовым коменте писать в подобном стиле, то и тут продолжим. А так — выбор имен переменных это наше все.
            • +1
              Нет. Это pure лямбда. У DancingOnWater было замыкание.
              • 0
                Да, и правда что.
            • 0
              А вот и нет :)

              В первом случае это была функция с замыканием — x и y ловились из контекста.
              В вашем случае это просто функция от двух переменных, а замыкание отсутствует.
        • +1
          ммм… Прочитал всю ветку, никто так и не задал этого вопроса. Простите, а зачем Вы показываете человеку, не знающему английского и далекому от программирования распечатку кода? И какая польза от того, что он уловит какую то часть логики кода, если этот код, прости господи, на языке 1С?
    • +2
      Это просто перегрузка оператора <<. Вы можете использовать put, или что еще там в api есть совершенно спокойно. Кстати говоря это еще очень простая особенность синтаксиса, любой программист должен знать, что << — оператор сдвига, и если увидеть вот такой код:
      std::string name;
      std::cout << "Enter your name: ";
      std::cin >> name;
      std::cout << "Hello, " << name << "!\n";
      

      То программист с легкостью поймет, что операторы сдвига каким-то образом позволяют считывать и записывать информацию, не зная о перегрузках, потоках и прочих C++ наворотах
      • 0
        А вот это, например, что, вывод, сдвиг, что-то ещё?
        std::cout << 1 << 1;
        • +4
          А в чем проблема с этим кодом? Если хочется сделать сдвиг — поставьте скобки, или запишите результат в переменную.
          int result = 1 + 2 / 3 * 5 - 11 + 12 / 15; — вот тут например тоже сразу не понятно что будет, надо напрячься и вспомнить про приоритеты операций, так что любые операции с операторами в любом языке можно написать понятнее — было бы желание)
          • +1
            Ну вообще-то дело в моём примере не в приоритетах, а в ассоциативности.
            А если хочется фокуса с приоритетами, то вот:
            std::cout << a & b;
          • +4
            Простите, но когда программист пишет, что ему надо напрячься и вспомнить про приоритеты арифметических операторов, я испытываю когнитивный диссонанс. Даже хотя прекрасно знаю о холиворах не тему значения выражения 2+2*2. Всё равно не могу привыкнуть к этому дивному новому миру.
            • –1
              не очень понимаю, в чем когнитивный диссонанс. Выражение, которое я указал выше, или подобный финт с тернарными операторами заставляет программиста задуматься там, где можно было бы этого не делать.
              • +2
                Извините, но в Вашем примере я вижу только голую арифметику. Это явно не тот пример, который должен демонстрировать вопросы сложности приоритетов.
      • –1
        > Это просто перегрузка оператора
        Скажите, а какая вообще связь между сдвигом и вводом-выводом? Какой логикой нужно было руководствоваться, чтоб использовать операторы сдвига для IO? Не монетку же Страуструп кидал, должно же быть хоть какое-то обоснование использования именно этих операторов, а не, скажем,! или:
        • +7
          Потому что "!" — восклицание, ассоциируется с отрицанием.":" у меня лично не с чем не ассоциируется. А вот сдвиг, учитывая то, что у нас потоки — вполне. Есть поток мы из него, или в него что-то сдвигаем. В чем проблема-то?
          • +1
            Проблема и в произвольном выборе оператора, и в не самом низком приоритете выбранного оператора (почему, например не оператор ","), и в костыльном методе задания формата вывода (вот, например, не глядя в документацию, сможете вывести через std::cout аналогично printf("%#10x", a)?).

            А вот так не судьба было сделать:

            std::cout.out(a).out(format-spec, b);
          • –2
            Сдвигаем в поток? Выдвигаем из потока? Для вас это имеет какой-то смысл? Для меня нет.
            • +4
              Объект перенаправить в поток\поток перенаправить в объект. Стандартная операция *nix-ов
              • 0
                А чего тогда не стандартные обозначения > и <?
                • +5
                  потому что << — добавление в конец потока, например echo blabla >> file.log
                  • –5
                    А, а << — это, стало быть, извлечение из конца потока? Или не из конца? Ведь < извлекает из начала. Или не из начала?
                    А почему у вас поток справа, а в c++ — слева?
                • +2
                  Потомучто это операции сравнения больше и меньше — еще более стандартное обозначение
                  • –1
                    Чтобы можно было один поток с другим сравнить? Или с объектом, чтобы понять, влезает он в поток, или не влезает?
                    • +1
                      Не понял вопроса
                      • –3
                        Операторы «больше» и «меньше» — зачем они потоку? Чтобы можно было один поток с другим сравнить? Или с объектом, чтобы понять, влезает он в поток, или не влезает? Я уже конечно совсем стебусь.
              • +1
                > Стандартная операция *nix-ов
                Да, вполне вероятно, что взято оттуда. Об этом не подумал.
        • +1
          должно же быть хоть какое-то обоснование использования именно этих операторов

          Так визуальная составляющая и была основой для выбора. Операторы напоминают стрелку. Собственно для обозначения сдвига они используются ровно по той же причине.
    • +1
      Иронично — когда я учился в колледже и нам преподавали C++, ввод/вывод с помощью cout/cin считался самым простым и годным.
      • +2
        Это пока не понадобится какое-никакое форматирование. А там — адов ужас в виде манипуляторов.
  • +8
    Не читал статью, но сразу хочу сказать — посмотрите доклад — Вы ничего не потеряете — Скотт ооочень интересно рассказывает.
    Спасибо за статью
    • +4
      Без сомнения доклад лучше. Статья — только для экономии времени.
  • +24
    Программа отрабатывает только те инструкции, которые вы написали, именно в том порядке, в каком вы заставили её это делать. Никаких закулисных игр. Такие вещи особенно важны в системном программировании.

    Простите, но это совершенно не верно. Я вообще не понимаю, как такое можно написать если хоть немного работал с C++. Точки следования, произвольное вычисление аргументов и прочие веселости явно говорят не в пользу написанного.

    По моему, именно закулисные игры в C++ это то, чему учится программист всю свою профессиональную жизнь.

    P.S.: Я понимаю что это пересказ, но в оригинале скорее всего Майерс говорил по другому.
    • +4
      Я уж точно не вспомню как он говорил, а пересматривать час видео — лень, но в общем главным тезисом было «вы не платите за то, что не используете». Если исключение не возникло — try\catch не стоит ничего. Если вы не вызвали удаление объекта сами — вы можете быть уверены, что никакой там «сборщик мусора» не решит внезапно это сделать за вас в самый неподходящий момент. А то, что в языке есть много случаев неопределенного поведения — ну, это факт. Но это ведь не по глупости или недосмотру, а для того, чтобы дать свободу компилятору генерировать быстрый код.
      • +1
        Вы не платите за то, что не используете
        Я хоть что-то сказал против этого? Я говорю что обозначенные мной утверждения могут ввести в заблуждение. К слову, неопределенное поведение это вообще отдельная тема. Тут речь о синтаксически и семантически корректных ситуациях, которые тем не менее, могут быть оформлены компилятором по своему усмотрению. Так что это ненуточняемое поведение.
      • +8
        Ващпе-то компилятор волен не только менять инструкции местами, но и целые блоки кода. К примеру, в gcc:
               -freorder-blocks
                   Reorder basic blocks in the compiled function in order to reduce
                       number of taken branches and improve code locality.
                   Enabled at levels -O2, -O3.
        

        Если исключение не возникло — try\catch не стоит ничего.

        Это зависит от модели исключений. Модель setjmp/longjmp тормозит программу, даже если исключение не возникло.
        • +1
          Модель setjmp/longjmp — это худшее, что вообще придумано на тему. Зачем её использовать, если уже давно все сделали лучшие альтернативы?
      • 0
        Если вы не вызвали удаление объекта сами — вы можете быть уверены, что никакой там «сборщик мусора» не решит внезапно это сделать за вас в самый неподходящий момент.

        Планировать работу сборщика мусора можно и в managed-языках (и опытные программисты с этим справляются). А вот утечки памяти — это чаще c/c++-ная проблема.
      • +4
        Если вы не вызвали удаление объекта сами — вы можете быть уверены, что никакой там «сборщик мусора» не решит внезапно это сделать за вас в самый неподходящий момент
        Это в чистом C++. Мало кто пишет без библиотек и фреймворков. Навскидку, объекты могут быть убиты:
        1. Умными указателями
        2. Всякими parent-like конструкциями из объектов
        3. Чем-то вроде сигналов-слотов в Qt
        4. Могут убиться сами по воле создателя какого-нибудь особо «умного» базового класса.
    • +3
      Мне вообще кажется, что без этой презентации Скотта статья будет неполной.
    • 0
      Во-первых, никаких точек следования больше нет.
      Во-вторых, то что во многих местах стандарт оставляет за реализацией право на некоторую свободу в действиях (при условии неизменности конечного результата) закулисной игрой назвать сложно.
    • +4
      Плюс не стоит забывать о тоннах неявно генерируемых функций — все эти конструкторы, деструкторы, операторы копирования по умолчанию трудно не назвать «закулисными играми».
  • –26
    Соответственно, что-то в этом языке сделано правильно. Почему-то он плывёт. Давайте разбираться почему.

    До этого места в статье всё было хорошо и интересно, особенно эта вставка про затонувший корабль.
    Однако стоило дойти до этого места — и тут же началось бесконечное перечисление вещей, «сделанных правильно», из-за которых он «плывёт».
    И вот тут у меня подозрение, что слишком много киллер-фич, чтобы это было правдой.
    Причём на каждую киллер-фичу отвели всего по три предложения.
    Если бы автор РЕАЛЬНО нашёл бы киллер-фичу, которая сделана правильно и почему корабль плывёт — она скорее всего была одна. И описана была намного более подробно — например, в сравнении с другими языками, которые не плывут дальше и другими языками, которые плывут.
    Поэтому скажу словами Станиславского: «НЕ ВЕРЮ!!»
    • –13
      Ни один из минусующих не хочет пояснить свою позицию словами?
      • +30
        Вы, наверное, «вылезли» из мира эппов для мобилок или «крутых» веб аппликаций, где самый крутой и важный (модный?) фактор, определяющий успех, это КИЛЛЕР-фича. Вот вам прям вынь да положь эту килллер-хрень, иначе никто пользоваться не будет.
        Странно, что до Вас не доходит, что серьёзная система это, прежде всего, комбинация разных факторов и возможностей, а не одна конкретная «киллер-хрень»
        • +7
          У С++ есть такая Киллер-Фича, на самом деле.
          Он супер-мощный и одновременно супер-портабельный.

          Не то, что всякая платформо-зависимая всячина, начиная с Objective-C (гибрид утконоса и ехидны си и смолтока, получивший распространение только в Apple) и кончая явой и дотнетом (для которых нужно тащить жирный рантайм — попробуйте-ка запихнуть его в микроконтроллер).
          • 0
            Таки зависит от мк — в некоторые влазит (понятно, что не полный). Вообще java не такой уж слабый язык в плане embed-устройств.
          • +1
            Вы наверное удивитесь, но Java рантайм стоит даже в вашей SIM-карте: ru.wikipedia.org/wiki/JavaCard
            • +10
              Как человек реально работавший с этим чудом в своё время могу ответственно вам заявить: никакой Java'ой там и не пахнет.

              То есть это даже не Dalvik, программы для которого хоть чем-то напоминают Java. Это — вещь куда «круче». Если её с чем-то и сравнивать — то разве что с BASIC'ом. Причём не с VB.NET и даже не с просто VB, а с тем самым BASIC'ом с первых восьмибитных персоналок.

              Сами подумайте: в этой Java отсутствует сборщик мусора. То есть совсем. Думаете взамен дали delete? Ага, щаз. Его — тоже нету. То есть вы за всё время жизни программы можете создать фиксированное число объектов ибо умирать они не умеют в принципе. И если вы думаете, что вы можете отработать запрос и умереть, то я вас, опять-таки разочарую: программа «запускается» при записи её на карточку и считается «работающей» пока её не сотрут.

              P.S. Разумеется всяких излишеств типа строк там или целых чисел тоже нету. Short'ы есть, да. Байты ещё. Живите — ни в чём себе не отказывайте. Java, ага.
              • 0
                Вы путаете Java как язык программирования, и платформы Java. Последних существует несколько: Java SE, Java EE, Java ME, Java Card и пр. Естественно разработка под каждую из них кардинально отличается, но это все равно Java.
                • +9
                  Судя по описанию выше, это настолько малое подмножество JVM, что общее там по факту только название.
                  • +1
                    JVM там нету совсем. Я не зря Dalvik вспомнил. JavaCard устроена точно также: сначала компилируем в байткод, потом конвертрируем результат. Если в байткоде что-то страшное встретилось (ну там кто-то synchronized написал или float себе завёл) конвертор ругается. Меня всегда удивляло, что во всех наездах на Android (типа он устроил фрагементацию в дружном мире Java) никто не вспоминает про JavaCard.
          • +2
            и кончая явой

            попробуйте-ка запихнуть его в микроконтроллер


            Взгляните:
            mcujavasource.sourceforge.net/ru/index.xhtml
            dmitry.gr/index.php?r=05.Projects&proj=12.%20uJ%20-%20a%20micro%20JVM
            en.wikipedia.org/wiki/Java_Card

            а вообще, куча есть штук. Кажется, даже плата есть специальная учебная, которая программируется на C# и жует .NET-овский байткод.
          • +1
            www.tinyclr.com
            www.netduino.com
            www.ghielectronics.com/technologies/netmf

            про java — она, например в каждой сим-карте присутствует практически, не считая кофеварок, стиральных машинок, и еще кучи вещей с микроконтроллерами
          • +1
            >>платформо-зависимая всячина, начиная с Objective-C

            Вы удивитесь, но реализации GNU под этот язык есть под Линукс и Windows. Да смачных фишек от еппл у них нет (АРЦ — на пример, ЮАй кит в библиотеке поставке).
      • –7
        Я бы тоже минуснул, если бы мог.
  • –11
    Давайте посмотрим вот на это:
    f(x);
    Вроде бы всё ясно ...


    Да, есть и еще интересный пример
    #include <iostream>
    
    double i = 3.14;
    
    void f() { std::cout << i << std::endl; }
    
    int main()
    {
      int(i) = 20;
      std::cout << i << std::endl;
      f();
      return 0;
    }
    

    Казалось бы тоже все ясно. И человек малознакомый с тонкостями C++ мог бы написать такой код в надежде на преобразование типа (в данном случае double к int), но тут не приведение типа, а объявление новой переменной, хотя и странно выглядящее…
    • +10
      Та ну, это Вы накрутили.
      Всем и так ясно, что локальная переменная i типа int, скрывает глобальную переменную i типа double и cout в main выведет 20, а cout в f выведет 3.14, поскольку он понятия не имеет о какой-то локальной переменной в другой функции :)
  • –2
    -
  • +18
    А мне статья не понравилась и вот почему.
    1. Заявление о том, что самая важная фича в Си++ — это декструкторы, на мой взгляд весьма неочевидно. Может быть это и так, но в статье данная тема не раскрыта, притом, что заявление очень и очень громкое.
    2. В статье неявно используется тезис, гласящий, что Си++ до сих пор на плаву главным образом потому, что он обладает такими хорошими свойствами. Совсем неочевидно, что этот тезис верен. Лично я склонен считать, что в плавучесть этого языка вносят весомый вклад также и внешние факторы, и история. Например, язык Си является куда более распространенным, но почти никто не сомневается, что он менее удобен для большинства задач, нежели Си++.
    3. В главе «Шаблоны» есть проблемы со связностью. Скорее всего предложения этой главы как-то связаны друг с другом, но мне, как читателю, эта связь кажется туманной. Стоило бы объясниться яснее.
    4. Толи я что-то не понял, толи диаграммы в некоторых вопросах конфликтуют с текстом. Заявляется, например, что Си++ — на втором месте по вкладу в OS. А на диаграммах прямо над этим текстом Си++ на третьем месте после Java и Си. Тут видимо тоже есть смысл, но он раскрыт плохо. Необходимы дополнительные комментарии автора.

    Общее впечатление о статье также плохое. Анализа мало, знамён и воодушевляющих речей много. А хотелось бы именно анализа, тема-то интересная…
    • +9
      1. Личное мнение Скотта, ничего поделать не могу. Я бы как самую важную фичу определил бы вцелом ООП (классы, уровни доступа, наследование, виртуальные функции, деструкторы), но Скотт сказал как сказал.
      2. В статье используется тезис, что С++ на плаву в том числе благодаря набору таких хороших свойств. Понятное дело, что есть и исторические факторы и вклад сильных программистов и подковёрные игры корпораций и много чего ещё.
      3. Перечитал раздел о шаблонах. Несвязанности не нашел. Аппарат разбился из-за несогласованности типов в коде. С++ даёт возможность контроля типов, один из инструментов для этого — шаблоны. Программу того аппарата можно было спроектировать так, что ошибка несогласованности систем мер и весов были бы невозможны.
      4. Перечитайте названия диаграмм, вклад в опенсорс — под текстом и там С++ второй.

      Анализа мало, поскольку до этого Скотт много выступал с чисто техническими докладами, где дотошно разбирал всякие моменты языка, а этот доклад был такой «евангелический».
      • 0
        А не могли бы вы дать ссылки на технические доклады, если они имеются?
      • –4
        Знаю c++ мало (никогда не программировал на нём коммерческий код), но:
        1. Деструкторы — вещь безумно полезная… на фоне проблем языков, существовавших до появления c++. В современном мире есть аналоги, которые могут успешно конкурировать. Он на этом акцент делал и вы опустили / он сознательно манипулировал фразой?
        3. Не очень знаком с шаблонами (и откровенно плохо их помню) в c++, но что-то не очень представляю как они должны спасти, если тип — везде масса. Либо же предполагается что-то вроде java-шного GregorianCalendar, который по умолчанию используется и остальных при явном определении (ну и даты, соответственно автоматически транслируются между календарями)?
        4.
        С++ на втором месте по количеству кода в opensource (12%) и этот процент растёт год от года.
        — на скриншоте сразу над этой фразой явно видно, что c++ постоянно занимает 3ье место (один раз 2ое), при этом на графике слева видно, что не растёт, а даже наоборот — падает.
        • 0
          Для непонятливых, смотреть надо на картинку что ниже, а ещё желательнее читать что написано на картинках.
          • 0
            — Качество картинок не способствует их внимательному чтению,
            — обычно поясняющий текст пишется под картинкой, в противном случае (когда картинка идёт под поясняющим текстом) в конце текста следует ставить ":". Если автор не хочет соблюдать стандарты форматирования — он получит недопонимания и это логично.
        • +5
          >> Деструкторы — вещь безумно полезная… на фоне проблем языков, существовавших до появления c++. В современном мире есть аналоги, которые могут успешно конкурировать.

          Например? Из известного мне это только scope guards, но они не покрывают все сценарии. GC аналогом не является (если вы думаете, что деструкторы нужны для управления памятью — это уже принципиальная ошибка).

          >> Не очень знаком с шаблонами (и откровенно плохо их помню) в c++, но что-то не очень представляю как они должны спасти, если тип — везде масса.

          Типом в данном случае будет не просто масса, а «масса в кг», и в случае использования масс с разными единицами, нужно будет явно приводить что-то к чему-то. В C++ это делается на шаблонах, и есть готовая реализация в виде Boost.Units. Вот кусок примера из их документации:

          quantity<si::volume>    vs(1.0*pow<3>(si::meter));
          quantity<cgs::volume>   vc(vs);
          quantity<si::volume>    vs2(vc);
          
          quantity<si::energy>    es(1.0*si::joule);
          quantity<cgs::energy>   ec(es);
          quantity<si::energy>    es2(ec);
          
          quantity<si::velocity>  v1 = 2.0*si::meters/si::second,
                                  v2(2.0*cgs::centimeters/cgs::second);
          }
          


          Соответственно, при умножении расстояния на время, например, у вас получается именно скорость, а не что-то иное — причем именно в той системе измерения, в которой были входные параментры. Если это разные системы, то вам придется написать явное приведение через static_cast, чтобы однозначно задать, в чем должен быть результат.

          В F# аналогичная, но более простая фича есть на уровне языка — возможно, с этим будет проще разобраться.
          • +1
            Например, для организации RAII не нужны никакие специальные конструкции, кроме первосортных функций. Их достаточно для того, что бы организовать RAII.

            Тем не менее, в C++ изначально не было первосортных функций, зато была придумана искусственная конструкция, имеющая смысл только в C++ версии ООП. Хотя язык гибридный и не принуждает к использованию ООП.
            • +3
              Вы имеете в виду аналог using в C#, который принимает функцию-параметр в качестве «тела»? Ну, для начала, это не эквивалент уже хотя бы потому, что в нем нельзя сделать return из внешней функции, или break из внешнего цикла. Но даже если забыть про это, каким образом это поможет вам в случае графа объектов, где при уничтожении владельца должны автоматически уничтожаться и все зависимые объекты?
              • 0
                Ну, для начала, это не эквивалент уже хотя бы потому, что в нем нельзя сделать return из внешней функции, или break из внешнего цикла.
                Тогда добавляем ещё first-class escapes (возможность передать в функцию объект, с помощью которого можно сделать return/break в нужное место) и получаем… Scheme.
                • 0
                  Но деструкторы полностью все равно не заменили (деревья придется обходить вручную).
                  • 0
                    Настолько же вручную, насколько поддержание дерева придётся «вручную» прописывать в деструкторе владельца или типе поля, хранящего зависимые объекты.
                    • 0
                      В деструкторе владельца не нужно явно вызывать деструкторы дочерних объектов.

                      А тип поля (если забыть про прочий профит от типизации) прописывается не ради деструктора, а ради конструктора — т.е. указывает на то, объект какого вообще типа мы создаем. Этого вы не избежите и в языке с динамической типизацией. А автоматический вызов деструктора там прилагается бесплатно, в отличие от.
                      • 0
                        Согласен. В общем, полноценные объекты функций + «аналог using» (+ dynamic-wind) позволяют получить конструкцию «выполнить X на входе в лексический блок и Y на выходе», для чего в Си++ RAII чаще всего и используется (всякие гварды, локи и т. д.)

                        Но вот какой-нибудь shared_ptr — тоже пример RAII, — на них не соорудить. Или ваше дерево объектов (если только сам объект-владелец не убивается при выходе из блока).
                        • +1
                          Как убивается объект-владелец, как раз не важно. Пусть даже это будет выход из блока. Но как он будет в свою очередь автоматически убивать то, чем владеет?

                          shared_ptr соорудить как раз вполне можно, но там будут ровно те же ограничения — если у вас есть только «using», то все ваши shared_ptr'ы должны будут быть локальными в каком-то блоке, или же на них придется где-то явно вызывать release.
          • –2
            Первоочерёдная задача деструктора — корректное закрытие объекта (потоков). С этим gc + обвязка вполне справляются. Вторичная — корректное «завершение» работы объекта может быть легко выполнено архитектурно — не вижу проблем с этой стороны. Хотя первое время, согласен, тяжело переориентироваться на такой подход.

            Окей, шаблоны из гуд (хотя того же можно достичь, опять же, архитектурно). Только такие примеры (расчёт скорости спутника) неплохо бы расписывать более явно (а не только для тех, кто язык итак знает). К тому же, как я уже говорил — там скорее архитектурная проблема (никто и не подумал, что масса / скорость может измеряться в других системах счисления).
            • +4
              >> Первоочерёдная задача деструктора — корректное закрытие объекта (потоков). С этим gc + обвязка вполне справляются.

              Не справляется с этим GC. С памятью — да, но большинство других ресурсов (мьютексы, сокеты, хэндлы памяти etc) должны быть освобождены детерминистично, и GC здесь не помощник. Или под этим вы имеете в виду…

              >> Вторичная — корректное «завершение» работы объекта может быть легко выполнено архитектурно — не вижу проблем с этой стороны.

              В таком случае, объясните, как именно это «легко выполняется архитектурно» без ручного управления временем жизни в отсутствие деструкторов (и, как следствие, RAII).

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

              Опять эта же непонятная фраза. Поясните на конкретном коде.

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

              Это именно та самая проблема, которая решается достаточной типизацией. Собственно, люди её решают так же, когда пишут единицы измерения после всех величин при рассчетах.
              • 0
                (2ой абзац) Посмотрите на AutoClosable интерфейс. Все необходимые операции завершаются вполне детерминистично. Аналог вторичного функционала деструкторов. Или для вас деструкторы — менее ручное, чем такое архитектурное решение?

                (3ий абзац) Абсолютный аналог — даты. Вот есть java.lang.Date, который содержит UnixTimeStamp, а есть Calendar и его субклассы типа GregorianCalendar и прочих, которые умеют UTS переводить в дату на выбранном календаре. Это в общих чертах. Это и является архитектурным решением.
                • +4
                  >> (2ой абзац) Посмотрите на AutoClosable интерфейс. Все необходимые операции завершаются вполне детерминистично. Аналог вторичного функционала деструкторов. Или для вас деструкторы — менее ручное, чем такое архитектурное решение?

                  Нет, это вполне нормально. Но, собственно, AutoClosable::close — это и есть деструктор. :)

                  С одним «но». Попробуйте на нем реализовать ситуацию, когда у вас не один объект, а дерево. Корень-то вы высвободите через AutoClosable автоматически, ок. А вот все «висящие» на нем объекты придется освобождать ручками в его close(). И так для каждого уровня дерева. Профит семантики плюсовых деструкторов в том, что они работают для всего, а не только для локальных переменных, причем автоматически — объявили переменную, получите вызов деструктора.

                  Кстати, вы, наверное, в курсе, что в C# аналогичная AutoClosable вещь называется IDisposable (и метод, соответственно, Dispose). Так вот там, C++/CLI отображает деструкторы ref-классов на Dispose в генерируемом коде. Но они сделали правильно, и оставили у них полную семантику плюсовых деструкторов — т.е. если в одном ref class есть поле типа другого ref class, и второй класс имеет деструктор (т.е. на уровне CLI — реализует IDisposable), то первый тоже автоматически получает IDisposable, который вызовет Dispose на дочерний объект.

                  >> Абсолютный аналог — даты. Вот есть java.lang.Date, который содержит UnixTimeStamp, а есть Calendar и его субклассы типа GregorianCalendar и прочих, которые умеют UTS переводить в дату на выбранном календаре. Это в общих чертах. Это и является архитектурным решением.

                  Это ничем не отличается от решения с шаблонами, кроме того, что вы напишете больше кода руками. Т.е. с вашим подходом вы вручную будете определять классы типа SiLength, SiTime и SiVelocity, и вручную же описывать, что SiLength/SiTime дает SiVelocity. А потом отдельно делать то же самое для ImperialLength etc, и опять же руками описывать преобразования. В Boost.Unit типа «скорость» как такового нет, это составной шаблонный тип из расстояния и времени, что отражает его единицу измерения (м/с) — ни одной строчки кода специально для него писать не надо было. Аналогично, из скорости при делении на время точно так же автоматически получается ускорение, и т.д. А для разных систем, достаточно задать преобразование между базовыми единицами данной величины один раз, и все производные также получают их автоматически. Т.е. если вы умеете переводить футы в метры, то это автоматически будет применимо к переводу из фт/с в м/с.

                  Собственно, шаблоны вообще — это штука, абсолютно перпендикулярная тому, что вы назвали архитектурой. Они просто позволяют описывать куда более сложные решения с меньшим объемом кода.
                  • 0
                    Но, собственно, AutoClosable::close — это и есть деструктор. :)

                    Тут вы не правы (если мы говорим про Java) — close всего лишь закрывает длительную операцию. Т.е. фактически освобождается какой-то занятый ресурс, но не уничтожается сам объект (что для деструктора является обязательным условием).

                    Вопрос с деревом решается как и в деструкторе (хотя мне не очень понятна область применения дерева относительно ресурсов) — каждый объект закрывает дочерние объекты и закрывается сам.

                    IDisposable и AutoClosable всё-таки разные (совсем) вещи. Все объекты в Java имеют метод dispose. Но есть причины по которым его не используют — JVM не гарантирует время вызова этого объекта. Поэтому, в отличие от C++ в Java различают ресурсы, которые надо закрывать детерминистично (мониторы, потоки, ...) и недетерминистично (сами объекты). Для первых необходимо использовать архитектурные решения, для вторых — можно оставить на совести GC.
                    • +2
                      >> Тут вы не правы (если мы говорим про Java) — close всего лишь закрывает длительную операцию. Т.е. фактически освобождается какой-то занятый ресурс, но не уничтожается сам объект (что для деструктора является обязательным условием).

                      Это вопрос терминологии — само слово «деструктор» четко не определено. Например, в C# им зачем-то назвали finalizer, который вообще ни разу не детерминистичный. Я его использовал в смысле «обратная сторона инициализации, которая обеспечивает все гарантии в RAII».

                      Объект действительно не уничтожается ни в Java, ни в C#, но он, как правило, переходит в состояние «невозможно использовать», когда все методы кидают исключения — причем в большинстве случаев это one-way ticket, т.е. вернуть его обратно в нормальное состояние невозможно. В C# это вообще прописано в контракте Dispose. С моей т.з, важное в деструкторе то, что он освобождает все ресурсы, требующие детерминистичного высвобождения (в данном языке). Соответственно, в плюсах это включает в себя и память, а в Java и C# — нет, поэтому и есть эта разница со временем жизни. Но на практике для RAII она совершенно не важна.

                      >> Вопрос с деревом решается как и в деструкторе (хотя мне не очень понятна область применения дерева относительно ресурсов) — каждый объект закрывает дочерние объекты и закрывается сам.

                      С деструкторами каждый объект не должен явно закрывать дочерние объекты, его деструктор автоматически позовет их деструкторы. Более того, такой деструктор вообще не надо определять (если у объекта нет своей логики освобождения ресурсов) — он сам появится автоматически при наличии полей, которые надо чистить. Это принципиальная разница с close(), который надо будет руками определять, и руками же звать на всех владеемых объектах.

                      Дерево с ресурсами — ситуация вполне типичная. Например, в UI-фреймворках, дерево обычно соответствует дереву виджетов, а их ресурсы — это оконные хэндлы (т.е. HWND на Win32, например). И при убивании корня дерева (обычно, окна верхнего уровня), нужно освободить вот это все.

                      Еще часто бывает ситуация, когда сам по себе граф объектов не представляет ресурсы, но у каждого объекта во владении есть еще один или несколько несвязанных объектов, которые надо прибить — например, по файлу на нос. Соответственно, у объектов в дереве нужен деструктор, просто для того, чтобы он позвал таковой на файле. Вообще, деструкторы — заразная штука, в прямом смысле :)

                      >> IDisposable и AutoClosable всё-таки разные (совсем) вещи. Все объекты в Java имеют метод dispose. Но есть причины по которым его не используют — JVM не гарантирует время вызова этого объекта.

                      Вы, кажется, путаете здесь dispose (которого в Java таки нет — точнее есть, но только у отдельных классов, например в AWT), и finalize (который есть и в C#, и в Java у всех объектов, но в C# для него используется особый синтаксис, хотя на уровне CLR там все равно Finalize). IDisposable — это именно самый что ни на есть прямой эквивалент AutoClosable, интерфейс и метод для детерминистичного высвобождения ресурсов, в сочетании с конструкцией языка для автоматического вызова этого метода.
            • +1
              Окей, шаблоны из гуд (хотя того же можно достичь, опять же, архитектурно)


              Это как же Вы архитектурно достигнете:
              а) вычислений во время компиляции
              б) автоматической генерации кода
              ?
              • 0
                И совсем забыл про в) полиморфизм на этапе компиляции.

                Это все к тому, что шаблоны в C++ ну оочень мощный интсрумент.
                • 0
                  а) и чем же так полезны вычисления во время компиляции?
                  б) это можно делать и в рантайме, но в общем-то… а в чём прикол?
                  в) тот же вопрос — какие бонусы получаем?
                  На примерах показать можете?
                  • 0
                    Я смотрю Вы никогда не работали с критичным к производительности кодом.
                    Делать некоторые вещи в рантайме может оказаться непомерно дорогим удовольствием. При этом нам по прежнему нужен толерантный к изменениям код. Тут на помощь приходят и статический полиморфизм (вспоминаем концепцию «стратегий» у Александреску А. «Современное проектирование на с++»), и вычисления на этапе компиляции.
                  • 0
                    Вы же согласны, что даже такой простой механизм, как позднее связывание, тоже может оказаться дороговатым удовольствием в некоторых случаях?
        • +1
          1. Деструкторы настолько полезная вещь, что даже в яве сперва от них отказались («память больше не ресурс»), а потом вдруг спохватились и вернули scope guard'ы в объектном стиле (спец.метод у «дежурного» объекта), а не в древнем процедурном __try — __finally

          Да, в плюсах неудобно писать процедурные scope guard'ы «по месту», но в бусте такая штука появилась десять лет назад, а с лямбдами там и буст не особо нужен.

          3. Шаблоны, как и вообще параметризованные типы (привет хаскеллу) позволяют делать разные чудеса. Но для понимания чудес надо всё-таки их изучить.
          Один вариант — это, в стиле Ады, просто запретить неявные приведения типов «масса-в-кг» и «масса-в-фунтах»
          Другой — реализовать это приведение корректно, с пересчётом значения.
          Тогда
          m_in_kg x;
          m_in_pnd y;
          x = y;
          будет превращаться в
          x.value = y.value * 0.454
      • 0
        >> С++ даёт возможность контроля типов, один из инструментов для этого — шаблоны. Программу того аппарата можно было спроектировать так, что ошибка несогласованности систем мер и весов были бы невозможны.

        Кто мешает завести различные типы для систем мер и весов, и зачем для этого применение шаблонов?
        • +1
          Замучаетесь вручную задавать типы для производных единиц, и преобразования между ними же в разных системах.
    • +6
      Про то, что самая важная фича — деструкторы, Скотт заявил, сразу же пояснив, что имеется в виду то, что именно деструкторы сделали возможной концепцию RAII, которую он называет UNDO-механизм.
      • –3
        Мы как-то к коллегами незлобно спорили о деструкторах и их важности.
        Я, как приверженец жавы, спросил — какую логику программы, если не считать освобождения ресурсов (памяти и прочих хендлов) можно/нужно поместить в деструктор? И если бизнес-логики нет — то не проще ли поручить деструкцию железяке?
        Внятного ответа на этот вопрос я не получил, коллеги мялись насчет того, что память надо сразу освобождать, а в деструктор можно вставить логгирование.
        • +5
          Хех, в деструктор можно много чего вставить. К примеру QMutexLocker в деструкторе разблокирует мутекс.
          • –13
            Это как в анекдоте про супружество: «Брак нужен людям чтобы сообща решать проблемы, которые бы не возникли в случае, если бы брака не было».

            Разблокировка мутекса в деструкторе — признак того, что блокировка его происходит в конструкторе.
            Не проще тогда отнаследоваться от мутекса тем или иным способом?

            (Да, есть еще варианты вольного обращения с мутексом туда-сюда и на всякий случай в деструкторе разблокировать — это говорит о лапше в алгоритме)
        • +7
          Самый наглядный пример: класс LongOperation, без единого метода, в конструкторе изменяет курсор на песочные часы, в деструкторе изменяет обратно на стрелку.
          Тогда в длительно работающей функции достаточно объявить переменную типа LongOperation где-то в начале, и больше её не трогать.
          И можно быть уверенным, что курсор изменится обратно при выходе из функции любым способом (return из середины, исключение, что угодно).
          • –2
            Я несколько не о том.
            Понятно, что раз объект создается и инициализируется, он при уничтожении должен как-то вернуть исходное состояние системы. Но нужно ли программисту для этого писать руками деструкторы? Конкретно в вашем же примере «достаточно объявить» — и остальное сделает за вас компилятор. Я не хочу разводить холивары — жава и с++ — я обоими этими яыками пользуюсь. И люблю их оба по-своему.
            • +5
              С точки зрения пользователя LongOperation — действительно, «всё сделает за вас компилятор». Как раз потому, что автор LongOperation написал руками деструктор.

              Мой предыдущий комментарий был ответом на ваш конкретно заданный вопрос: «какую логику программы, если не считать освобождения ресурсов (памяти и прочих хендлов) можно/нужно поместить в деструктор?»

              Память компилятор может освободить и сам, но всё более высокоуровневое состояние (курсоры, мутексы, сетевые соединения, блокировки в базе данных) освобождать придётся программисту. Руками, да.
        • +1
          «Надо выбирать себе умных коллег»
          В.Маяковский
        • +1
          В дестркуторе можно выполнить любой код, который нужно гарантированно выполнить при выходе из области видимости ( запись в лог, в файл, освобождение ресурса, отправка сообщения по сети ).
          • 0
            >>В дестркуторе
            >>отправка сообщения по сети
            За такое по рукам надо бить
  • +10
    Шаблоны настолько замечательная вещь, что можно потратить пару дней, пока напишешь абстрактный код, работающий так, как надо.

    А еще ошибки в коде с шаблонами очень замечательные. Длинные.

    Утверждают, что для следующего кода компилятор выдаст ошибок на примерно 16 килобайт.

    #include <vector>
    #include <algorithm>
    int main()
    {
        int a;
        std::vector< std::vector <int> > v;
        std::vector< std::vector <int> >::const_iterator it = std::find( v.begin(), v.end(), a );
    }
    


    • 0
      У меня только 2.5 Кб) Может от компилятора зависит (пробовал на VS 2013 Pro Update 2).
      лог ошибки
      1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(3026): error C2678: binary '==': no operator found which takes a left-hand operand of type 'std::vector<int,std::allocator<_Ty>>' (or there is no acceptable conversion)
      1> with
      1> [
      1> _Ty=int
      1> ]
      1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\system_error(410): could be 'bool std::operator ==(const std::error_condition &,const std::error_code &) throw()'
      1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\system_error(402): or 'bool std::operator ==(const std::error_code &,const std::error_condition &) throw()'
      1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\exception(507): or 'bool std::operator ==(const std::exception_ptr &,std::nullptr_t)'
      1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\exception(502): or 'bool std::operator ==(std::nullptr_t,const std::exception_ptr &)'
      1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\exception(497): or 'bool std::operator ==(const std::exception_ptr &,const std::exception_ptr &)'
      1> while trying to match the argument list '(std::vector<int,std::allocator<_Ty>>, const int)'
      1> with
      1> [
      1> _Ty=int
      1> ]
      1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(3045): see reference to function template instantiation '_InIt std::_Find<_InIt,_Ty>(_InIt,_InIt,const _Ty &,std::false_type)' being compiled
      1> with
      1> [
      1> _InIt=std::vector<int,std::allocator> *
      1>, _Ty=int
      1> ]
      1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(3054): see reference to function template instantiation '_InIt std::_Find<std::vector<int,std::allocator<_Ty>>,_Ty>(_InIt,_InIt,const _Ty &)' being compiled
      1> with
      1> [
      1> _InIt=std::vector<int,std::allocator> *
      1>, _Ty=int
      1> ]
      1> c:\users\i.zimaev\documents\visual studio 2013\projects\consoleapplication1\consoleapplication1\source.cpp(13): see reference to function template instantiation '_InIt std::find<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<std::vector<int,std::allocator<_Ty>>>>>,int>(_InIt,_InIt,const _Ty &)' being compiled
      1> with
      1> [
      1> _InIt=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<std::vector<int,std::allocator>>>>
      1>, _Ty=int
      1> ]
  • +38
    потому, что С++ не тонет
  • +4
    Есть итересное обсуждение Generate the longest error message in C++ из которого можно почерпнуть много рецептов на тему «как прострелить себе что-нибудь побольнее».
    • +2
      О б-же, это восхитительно.
      Больше всего понравился пример, по заявлениям общественности сегфолтящий gcc:
      #define A(s) s##s##s##s##s##s##s##s
      #define B(s) A(s##s##s##s##s##s##s##s)
      #define C(s) B(s##s##s##s##s##s##s##s)
      #define D(s) C(s##s##s##s##s##s##s##s)
      #define E(s) D(s##s##s##s##s##s##s##s)
      #define F(s) E(s##s##s##s##s##s##s##s)
      #define G(s) F(s##s##s##s##s##s##s##s)
      a G(foo)
      
  • +26
    В не очень отдаленном будущем человечество будет колонизировать другие планеты — мне всегда было интересно, на каком языке будет написан бортовой софт звездолетов))

    Ну а если серьезно, то все это холивар от начала до конца! Почему С++ не утонул — ну а почему PHP не утонул, и живее всех живых, это показатель крутизны языка?? Мир IT — это сложнейшая экосистема, где действует множество факторов, в т.ч. имеющие к фичам языка весьма опосредованное отношение (legacy-код, всякие коммерческие интриги корпораций, порог вхождения в язык и многое другое).
    Почему визуальные языки так и не взлетели? Почему о невероятной мощи функциональных языков из поколения в поколения ходят легенды, но за эти годы (!!!) никто так и не построит на них крутой бизнес, оттяпав приличный кусок рынка, создав более-менее ощутимое количество рабочих мест? Почему существуют целые кланы Microsoft'о-ненавистников, которые задачу типично Enterprise сектора (много UI, бизнес-логики, базы, сети) будут до посинения кодить на C++, хотя именно для этого случая C# просто идеален, и позволил бы решить задачу в разы быстрее? Почему есть те, кто просто ненавидит Паскаль, или наоборот, поклоняется Вирту, и верит в гуляющую по сети байку про то, что С++ создан как шутка, и писать на нем невозможно?
    Мир людей не идеален, часто далек от логики, и нам предстоит еще много работы чтобы программисты могли ПРОСТО ПИСАТЬ КОД, забыв о технологических заморочках, проблемах кроссплатформенности, неподъемном синтаксисе, холиварах «какой язык лучше» и пр.
    • 0
      Толи у Винджа в «Пламени над бездной», толи у Симмонса в «Гиперионе» есть пассаж, что в далеком космическом будущем в основе программ, на которых работает искусственный интеллект, лежит код древней операционной системы Windows. ^)
      • 0
        Вообще-то, емнинмс, юникс. Было там что-то про счетчик времени в nix-формате.
        • 0
          Да, точно. Отсылка на unix-time там была.
      • 0
        У Винжа в «Глубина в небе» (приквел «Пламени над бездной») была остылка на софт из нашего времени, но, на сколько я помню, там была речь не про Windows, а про что-то из Unix. Кажется там было что-то от сокетов, могу ошибаться. И еще там была самая высокооплачиваемая профессия «программист-археолог» )
      • +3
        Фам Нювен несколько лет провел, обучаясь программировать и исследовать. Программирование восходило к началу времен. Как та навозная куча за замком отца. Когда ее промыло ручьем на десять метров в глубь, обнаружились искореженные корпуса машин – летающих машин, как говорили крестьяне, еще от тех великих дней колонизации Канберры. Но та навозная куча была чистой и свежей по сравнению с тем, что лежало в локальной сети «Репризы». Были программы, написанные пять тысяч лет назад, когда человечество еще не покинуло Землю. И самое чудесное (самое ужасное, как говорила Сура) было то, что, в отличие от бесполезных обломков прошлого Канберры, эти программы все еще работали! И через миллион миллионов запутанных нитей наследования многие из старейших программ все еще выполнялись во внутренностях системы Кенг Хо. Например, методы слежения за временем у торговцев. Поправки вносились неимоверно сложно – но на самом дне лежала крошечная программа, которая гоняла счетчик. Секунду за секундой отсчитывала система Кенг Хо с того момента, как нога человек ступила на Луну Старой Земли. Но если приглядеться еще пристальнее… начальный момент был миллионов на сотню секунд позже; момент «ноль» одной из первых компьютерных операционных систем Человечества.
        • 0
          Спасибо за цитату.
    • +7
      Почему С++ не утонул — ну а почему PHP не утонул, и живее всех живых, это показатель крутизны языка??
      Вы не поверите, но да, именно так.

      Только у него другая киллер-фича. PHP — это язык всеми фибрами заточенный на то, чтобы говнопрограммисты писали на нём говнокод. Или, если быть точным, чтобы люди не умеющие программировать могли-таки сваять нечто, что будет выдавать правильный результат в случае если пользователь будет аккуратен и не будет пытаться оную программу сломать.

      Разумеется обратной стороной медали является тот факт, что написать на нём хорошо работающую программу без ошибок и без уязвимостей куда сложнее, чем на Java или там C++, но с этим ничего не поделать, да. Это — прямое следствие его киллер-фичи.
  • +11
    Мне в своей жизни приходилось писать на многих языках, но только с C++ мне казалось, что я не программирую, а борюсь с языком. В основном это было связано с преобразованием типов данных.
    • 0
      И в чем были проблемы?
      • 0
        наверно в winAPI
      • –11
        Попробую объяснить по другому. Как-то мне была поставлена задача запрограммировать синус (sin) на ассемблере. Несмотря на то, что в других языках (в т.ч. в C++) это было бы банально (там уже все реализовано), мне все равно было интересно решать это задачу. С другой стороны, когда мне в C++ нужно просто присвоить значение одной переменной другой, но я этого не могу сделать, т.к. нужно применить какое-то хитрое преобразование типов, мне становиться грустно. Мне не интересно решать задачи по преобразованию типов.
        • +5
          Честно говоря из этого ответа ничего не понятно, можете привести пример если не сложно.
        • +2
          Просто не нужно приводить не приводимое и выдумывать костыли с рядом reinterpret_cast, static_cast, const_cast, что бы потом не говорить что на C++ легко выстрелить себе в ногу. И если не нравится строгая типизируемость, можно писать на языках с динамической типизацией.
          • +3
            С++, и Си, НЕСТРОГО типизируемые языки, в всего лишь со статической типизацией — habrahabr.ru/post/161205/. Вот это и грустно (
    • 0
      А также с 33мя типами конструкторов (это самый мрак) и перегруженными операторами, которые вовсе не то, чем кажутся. А ещё с продумыванием интерфейсов и размышлением над тем, что от чего унаследовать вместо написания логики.
      • 0
        Сожалею, но язык здесь не причем, С++ никого не обязует делать 33 конструктора и перегружать операторы неправильно, а вот в C# беда.

        Допустим я хочу перезагрузить оператор сравнения. Вместе с ним я должен перегрузить метод Equals и метод Hash.
        Также я должен помнить, что по дефолту для class оператор == сравнивает ссылки, а struct должен сравнивать по значению.

        Ну или, пишу я шаблон в С++ и там такой код:
        template <typename T> class A
        {
           void fu( T value)
           {
              int count = value.Count();
           }
        ]
        

        ну не будет у типа метода Count — не беда, ругнется компилятор и все, что я должен сделать в шарпах:
        class A<T>  where T: IList
        

        и т.д.

        • 0
          Он обязует помнить, как описывается каждый из них.
          • 0
            Ужас какой, гдеж это видано что программист должен знать язык на котором пишет.
            • 0
              Дело в объёме. Программа на C++ никогда не делает то, что написано. Вместо этого делается 100500 неявных операций и преобразований. C++ вообще рекордсмен по числу всяких неявных действий и действий по-умолчанию. И чтобы избежать ошибок, всё это надо держать в голове разом. Я имею достаточно богатый опыт работы с чистым C, и то, синтаксис описания указателя на функцию я до сих пор подглядываю в Google, потому что он контринтуитивен и не запоминаем. Что уж говорить про C++.
              • +1
                Вместо этого делается 100500 неявных операций и преобразований. C++ вообще рекордсмен по числу всяких неявных действий и действий по-умолчанию.

                0_0 пример.
                • 0
                  Ну сравните правила неявного преобразования типов в C++ и в Java, например.
                  • 0
                    И? пока это никак не доказывает утверждение.
              • +1
                Зачем держать это всё в голове? Пишу на С++ уже больше 8 лет. Никогда не имел сложностей о которых вы говорите (я про 100500 операций и преобразований), а что насчёт указателя на функцию так это вообще умора.
        • 0
          Я бы скорее отнес идею «where T: IList» к преимуществам, чем недостаткам. То, как это может выглядеть в C++ с концептами, и как выглядит в Rust с trait и typeclasses очень полезно при написании самодокументирующегося кода.
          Бывает сложно ясно выразить кодом, какими конкретно свойствами должен обладать тип шаблонного параметра.
          • +1
            Возможно, не спорю, но мне пока это только мешает. Иными словами, обязательство следовать этому стилю — зло.
  • –4
    А почему ABAP еще не плаву?
    Моё мнение достаточно просто — сейчас языки не умирают из-за legacy-кода.
    Все отмершие языки либо умерли в 70-80-e (ada, cobol, ...), когда кода по понятным причинам было значительно меньше чем сейчас, да и почти под каждую платформу был свой код, переход на новую машину сопровождался переписыванием кода под её архитектуру.
    Либо мертворожденные языки (D, Go, Smalltalk, тысячи их), которые собрали узкое коммьюнити, но так и не взлетели.
    • +3
      Как минимум про Go позволь не согласиться.
      • +1
        Пруфы?
        • +3
          Я считаю что «мертвость» языка определяется количеством коммерческих и бесплатных проектов. Как минимум в моем городе мне предлагали работу на проекты где есть Go.
          На гитхабе есть много Go библиотек, фреймворков, которые поддерживаются. Есть возможность писать GUI апликухи.
          По Go выпускаются книги и мануалы.
          Помимо того что язык используется Google, на хабре проскакивали статьи о успешном использовании Go в продакшине.
          Конечно комьюнити не громадное и специалистов в Go не пруд пруди, но это объясняется не мертвостью языка а его молодостью. Комьюнити активно растет.
          Так что мертворожденным его у меня язык не поворачивается назвать.
          • –1
            Гоу точно не мертворождённый, время от времени я сталкиваюсь с его использованием и сам использую.
    • –2
      Smalltalk дал начало Obj-C, который взлетел. И ещё другим дал.
      Насчёт D — они заведомо пошли по неправильному пути, сделав первый компилятор только для windows, затем только для x86, и вот, наконец, недавно начали заниматься ARM и даже открыли исходники. Так дела не делаются, вот и ушёл поезд.

      А про Go это вы вообще зря, изучите тему получше.
      • –1
        www.tiobe.com/index.php/content/paperinfo/tpci/index.html
        30 Go 0.425
        31 COBOL 0.407
        32 Ada 0.403
        33 Fortran 0.371
        34 ABAP 0.357
        • +5
          Блин когда уже перестанут воспринимать рейтинг на tiobe как популярность языка?
          • 0
            langpop.com/ про многие, в том числе и Go, и не слышал.
          • +8
            Посмотрел на рейтинг и понял, что пора с python переходить на Visual Basic, ибо тренд очевиден.
        • 0
          По количеству созданных за 2013 год репозиторив на GitHub:

          1 JavaScript 534943
          2 Ruby 408076
          3 Java 331224
          4 PHP 258187
          5 Python 212943
          6 C++ 155682
          7 C 138852
          8 CSS 122017
          9 Objective-C 73129
          10 C# 70700
          11 Shell 64398
          12 Perl 32764
          13 CoffeeScript 28642
          14 Scala 18690
          15 Go 17530
          16 VimL 14236
          17 Haskell 10287
          18 Clojure 10181
          19 Lua 9117
          20 Puppet 8790
          • +2
            А можно эти же данные, но с фильтром «не менее 5 пушей»?
            • 0
              Не знаю, я не очень силен в запросах к Google BigQuery, вы можете попробовать сами: www.githubarchive.org/. Но если поменять событие «создание репозитория» на «пуш», то существенно порядок не меняется.
            • +7
              1 JavaScript 394460
              2 null 380178
              3 Java 226876
              4 Ruby 184655
              5 Python 178185
              6 PHP 168886
              7 CSS 119688
              8 C++ 87982
              9 C 81779
              10 C# 57084
              11 Shell 50228
              12 Objective-C 48252
              13 Perl 24337
              14 CoffeeScript 18734
              15 Go 15221
              16 Scala 13548
              17 R 10583
              18 Haskell 10482
              19 Clojure 8883
              20 Lua 8732
              21 VimL 8109

              SELECT repository_language, count(repository_name) as cnt
              FROM (
                SELECT repository_language, repository_name, count(repository_name) as pushes
                  FROM [githubarchive:github.timeline]
                  WHERE type="PushEvent" AND PARSE_UTC_USEC(created_at) >= PARSE_UTC_USEC('2012-04-01 00:00:00')
                  GROUP BY repository_name, repository_language
                  HAVING pushes > 4
              )
              GROUP BY repository_language
              ORDER BY cnt DESC
              

              null появился ибо забыл про репозитории без указанного языка.
              • +2
                Спасибо.
                Только это немного другой рейнитнг (изначально хотелось созданные в 13ом году с пушами в том же году), и ещё вы дату забыли ограничить :)
                Не меньше 5 пушей за 13ый год рейтинг получился такой:
                Row repository_language cnt
                1 JavaScript 199244
                2 null 181928
                3 Java 109790
                4 Ruby 93656
                5 Python 87315
                6 PHP 83102
                7 CSS 52959
                8 C++ 43332
                9 C 41054
                10 C# 26365
                11 Shell 24387
                12 Objective-C 23033
                13 Perl 11670
                14 CoffeeScript 8849
                15 Go 7392
                16 Scala 6682
                17 Haskell 5066
                18 R 4369
                19 VimL 4317
                20 Clojure 4295

                Т.е. живых репозиториев за 2013ый год видно, что тот же Go хоть и сильно отстаёт, но в 20ку залез :-)
                А вообще много интересных данных получается:
                — платформ-зависимые аналоги c++ — Objective-C + C# в сумме обгоняют его :-)
                — js — самый популярный на этом ресурсе скриптовый язык, а не язык общего назначения / системный.
                • 0
                  Я бы не назвал ни Obj-C, ни C# «платформо-зависимыми аналогами C++». Obj-C еще можно так назвать с натяжкой, если вспомнить Obj-C++. А так это сильно разные языки с сильно разными возможностями; из общего у них только немного синтаксиса.
                  • +1
                    Я имел в виду несколько иное — Obj-C и C# — проприетарные языки, полноценно работающие только с одной платформой (Mac и MS соответственно). Вот эти вот языки в сумме смогли обойти универсальный c/c++ (не сумму этих языков).
                    • +2
                      C# стандартизирован в ECMA, благодаря чему существует такая вещь, как Mono.
      • +5
        >Smalltalk дал начало Obj-C, который взлетел

        Я извиняюсь, но все же на мой взгляд довольно сложно назвать «взлетанием» ситуацию, когда язык держится на плаву исключительно благодаря пинкам одной-единственной компании. Это больше похоже на жонглирование мячом: как только его перестанет подпинывать тот, кто им жонглирует, мяч тут же упадет и никуда не полетит.
        • –7
          То же самое можно сказать и про C++, мол, язык держится на плаву только благодаря тонне легаси.
          Взлетел != адекватен.
          • +4
            >То же самое можно сказать и про C++, мол, язык держится на плаву только благодаря тонне легаси

            1. В каком месте это «то же самое»? Совершенно разные, ничем не похожие друг на друга ситуации.
            2. Не хотите ли вы сказать, что на C++ не начинают новых проектов?
          • –1
            Тонна легаси сама по себе никуда не пропадет.
  • 0
    Про ногу — классно сказано!!! Просто, про меня слова.… и мощь устрашающа.
  • +1
    Доклад отличный. Но… За этот час с небольшим раз 80 на автомате хватался за мышь в попытке убрать-таки этот курсор со слайда :)
  • +16
    Читаем все про C++, пускаем слезу. А потом смотрим на джаву. Прямая как рельса, ООП без права на помилование. Ни лямбд ни функциональщины, ни малейшего сахарку (я сейчас про то, на чем пишет большинство). Сборщик мусора, который плевал на ваши потребности по быстродействию. Ну, кроссплатформенность, да.

    И что мы видим? Второе — четвертое место по разным метрикам.

    Я к чему. Может не надо упрощать и выводить распространенность языка от наличия в нем фич?
    • 0
      похоже, Майерс хотел показать, что чем больше всякого г-на в языке, тем лучше плавучесть. а вашим java с рельсами, сей королевский перфекционизм в отношении парадигм еще выйдет таким боком… креном. вот увидите)

      если серьезно, мне кажется, что секрет долголетия С++ в наличии спецификаций, не привязанных к конкретной реализации компилятора, их полнейшей открытости, педантичном отношении к обратной совместимости и умеренности темпа изменений — это с лихвой окупает все накопившиеся сложности и несуразности языка. а папские замашки, сначала Sun, затем Oracle, всю жизнь играли с Java злую шутку.
  • +2
    В общем-то ничего лучше C\C++ не придумали ни по его универсальности, ни по его оптимизированности. И мне сложно понять что же сложного и непонятного в самом красивом и функциональном языке? Я начинал свой путь в программировании на x86 именно с него, если не считать BASIC под z80. И мне, школьнику, было всё куда понятнее и логичнее, чем кривотяпный паскаль. В последствии появятся Java и прочие языки, попылярные сегодня, даже промышленное ПО будет писаться на, по сути, скриптовых языках, но ни один из них не приблизился по мощи и быстродействию, и, при должном вниманию к переносу — универсальности к C++.

    Приятно читать статьи и понимать, что язык не только жив и популярен, но только движется и развивается не только в open-source комьюнити.
    • +2
      Вот кстати, исходя из моего опыта, на базе C++ перейти на другой язык или хотя бы понять код другого ЯП значительно проще, чем имея базу на другом языке.
      К слову сказать, что моя невеста, далёкая от IT и тем более программирования, в рамках институтской программы имела дело с C++. И теперь, если ей для её инженерных расчётов нужно ознакомиться или поправить чужой код на другом языке, скажем Simulink, Python, Lua или чем-то ином то она, с большой долей вероятности сделает это по наитию и окажется права.
      • +5
        Это напоминает девиз МакДоналдс по найму сотрудников: «Поработав у нас, вы научитесь успевать и тут, и там...». Этот девиз у меня в голове всегда расшифровывался так: «Вы так задолбаетесь у нас работать, что всё остальное покажется вам полной фигней»
        • +1
          На такие мнения у меня обычно две ассоциации:
          1) «Математика тем уже хороша, что она ум в порядок приводит». (Михаил Ломоносов). Если перефразировать, то все возможности выстрелить себе в ногу или дебажить проект в сумме дольше, чем время разработки решаются системным подходом и вниманием. Да, Си++ в этом отношении для непосвящённых опаснее, но зная внутреннюю кухню, стандарты, кодинг гайды, паттерны, вы, вероятно, не только сможете решать разносторонние задачи оптимально, но и и избегать ошибок и легко переходить в другие области и языки.
          2) В каком-то роде С++ и, допустим, Java можно представить в виде аналогии сравнения Linux и Windows. Вроде и бизнес-цели одинаковые, а внутренности и возможности, перфоманс совсем разного уровня.
          • 0
            Вот, кстати, про перфоманс не стоит — java активно догоняет и уже не так уж и далеко по нему от системных языков. Хотя в некоторых областях, безусловно, до сих пор проигрывает из-за особенностей архитектуры.
            • 0
              То, что со временем Java будет догонять — это закономерно. Но если я всё правильно понимаю, никогда не будет быстрее, т.к. есть JVM, а не прямое транслирование. Скомпилированный код же C++ очень часто эквивалентен оптимальному решению на ассемблере.
              • +2
                Помниться был пост от разработчика JGit, где он сравнивал производительность свое детище и классический git. Его резюме что Java-версия gita всегда будет медленее Сишной, потому что есть ряд критических мест, где возможность сделать reinterept_cast решает дело.

                Работая с C# на подобные моменты натыкался и я.
                • +2
                  Да что там говорить — большинство системных вызовов на Java дороже, чем в сях — тут ничего не поделаешь и от накладных расходов не избавишься. Вопрос только в том, насколько реально медленней — на доли процентов, на проценты, в разы… Когда-то было в разы. Сейчас доли процентов и проценты (в зависимости от задачи).
                  • +2
                    Хмм, помнится был пост разработчика на C#, который рассказывал, как отказавшись от многих фишечек (из запомнившегося они отказались от foreach в пользу простых циклов) они получили прирост производительность в разы круче.

                    Так вот проценты\доли процентов это с фишечками или без оных. Если без — то нафига козе боян?
                    • 0
                      Проценты/доли процентов — это, в первую очередь, в зависимости от задач. Если задача связана с кучей системных вызовов и сложной арифметикой — проценты, если простая арифметика — доли процентов и т.д.
          • +1
            Да я, вроде, и непротив. Моя мысль лишь в том, что учить C++ только ради головоломной сложности — глупо. Нужны какие-то другие мотиваторы.
            • 0
              Хм, универсальность?
              Отсутствие потребности и привязки к окружению и ОС?
              Можно целый ряд мотиваторов найти, если задаться целью.
              • +2
                Универсальность C++ достигается примерно одинаково малым удобством разработки различных продуктов, кроме, разве что, низкоуровневого программирования — ОС, встраиваемые решения и т.п. Всё остальное писать на C++ заметно сложнее и дольше, чем на других языках в силу излишней многословности синтаксиса по сравнению с другими языками, малого количества качественных библиотек и отсутствию возможностей, опять же присутствующих в более современных языках.

                Отсутствие привязки к окружению и ОС достигается на самом низком уровне. Да и библиотеки, которые вы используете, кто будет портировать? В силу сложности и богатства языка качество их кода будет варьироваться в широчайших пределах, а наличие большого количества undefined behavior в C++ вносит дополнительный элемент непредсказуемости трудозатрат на портирование. Между тем, подавляющее большинство библиотек Java просто работает на том же Android без каких-либо правок исходного кода, написанного даже довольно криворукими разработчиками. Напоминаю, что написать корректно портируемый код на C++ значительно сложнее, чем на Java, а это напрямую влияет на количество кода, который будет портирован без проблем.

                Видите ли, не всё так радужно в мире C++, как любят представлять его адепты. Сам я пишу и на C++, и на Java, и на D, периодически решаю небольшие задачи на других языках. Мой опыт говорит не в пользу C++. Единственное место, где он для меня удобнее — микроконтролллеры (embedded).
      • +2
        ЯП бывают разными. База на Си++ будет выносить Вам мозг, когда Вы начнёте работать с функциональными языками. Я не утверждаю, что функциональные языки — это мегакруто, но они просто другие, и привычка к Си++ будет мешать их понимать. Да, сейчас в Си++ есть lambda, но, обычно, функциональные языки богаче на механизмы манипулирования кодом, и там эти механизмы на полную катушку используются. И имея опыт только Си++ сложно читать даже ту же Scheme, не говоря уже о Haskell.
        • 0
          Справедливое замечание. Как только появился Java, попробовать и пересесть на его прямолинейные рельсы совсем не было проблемой, то же с C#.
          Несколько иное дело со скриптовыми языками — там всё-таки другие цели, хотя для всего там опыта в cи хватало за глаза. Тем не менее, мне всегда хотелось попробовать Haskell, но он никак не связан был с моей работой, а времени на его изучения всегда не находилось. При этом просто взять и написать на нём оказалось нетривиальным делом для меня, во всяком случае, что-то более сложное, чем Hello world. Так, в итоге, и осталось.
    • +8
      И мне сложно понять что же сложного и непонятного в самом красивом и функциональном языке?

      Считать С++ самым красивым языком может лишь тот, кто незнаком с другими языками. Нет, это не камень в ваш огород. Когда-то я тоже думал, что С++ — это самый красивый язык, ведь ничего кроме него я не знал…
      • +1
        Потом появился Haskell, и всё закрутилось-завертелось… ;)
        • 0
          Потом появился Haskell, и всё закрутилось-завертелось… ;)

          Не только. Да, Haskell, по моему мнению, на порядок красивее чем С++, однако я имел в виду не только его. Например, код на Objective-C мне тоже видится более красивым, чем С++-код. Впрочем, красота — это всегда IMHO… ;-)
          • +3
            код на Objective-C мне тоже видится более красивым
            OH NOES!
    • 0
      в самом красивом...

      Возьмём, например, новые сигналы/слоты из Qt5:
      connect(sender, &Sender::valueChanged,
                   receiver, &Receiver::updateValue );
      

      Вроде красиво.
      А вот так?
      connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
                    slider, &QSlider::setValue);
      


      Страшная красота! :)
      • +1
         connect(a, &AView::resized, [this](const QSize& sz)
            {
              ...
            });


        А если так?
        • +1
          В простом случае читается, но вот в таком примере уже не очень:
           QObject::connect(socket, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error), [socket] (QAbstractSocket::SocketError) {
                  qDebug()<< "ERROR " << socket->errorString();
                  socket->deleteLater();
              });
          

          Отсюда: qt-project.org/wiki/New_Signal_Slot_Syntax
          • +1
            Почитал про перегрузку в сигналах-слотах. Да обидно, что синтаксис ухудшился из-за этого.
  • +3
    Нам говорят, что в стандарте языка С++ в 1990-ом году было 400 страниц, в 1998 — 700, а в 2011 стало 1300. «Язык становится сложнее» — нам говорят. Нет, не становится. Да, мы вводим новые понятия и фичи, но большинство их них призваны не усложнить, а упростить код. Вспомните, как вам приходилось бегать итераторами по векторам — и вот в С++11 у нас есть range-based for и auto. Мы можем написать одну строку кода там, где раньше было 5. Лямбда-функции дали возможность исключить из кода целые сущности — лишние классы и методы, которые по факту были не нужны. Да, разработчикам стандарта приходится обдумывать и писать существенно больше текста. Но конечному программисту становится только легче.

    Ага, легче, особенно когда ему приходится помнить и старые возможности С++ (для поддержки легаси-кода) и новые возможности С++. Например, раньше было четыре основных способа передачи объекта в функцию, а теперь — пять. Раньше инициализация членов-данных была в списке инициализации конструктора, а теперь ещё и in-place. Ну и в том же духе. В итоге несчастному новичку С++ приходится впитывать всё больше и больше информации, чтобы успешно пройти полное тестирование по этому языку…
  • –1
    После таких статей хочется все бросить и вернуться к программингу.

    Дайте мне сложную задачу, чтобы я мог отжечь на C++!!!

    • –3
      Напишите систему мира.
  • +3
    По моему скромному мнению C++ живет из-за того, что это единственный абсолютно неограниченный general purpose language, на котором можно писать что угодно от загрузчиков ОС и до игр.

    А теперь следите за руками:
    Конечно, у него куча тяжелого наследия (например, чудовищьный сишный линковщик — кому неясно, в чем чудовищность, поглядите на Паскаль или C#) Но его поддерживают, потому что всем понятно, что такой язык должен быть. Даже если требования к разработчику на нем выше, чем в среднем по отрасли.

    Проблема в том, что чтобы сделать «новый C++», несовместимый со старым (и, благодаря этому, лишенным всех его недостатков), требуется слишком много сил и времени. За это просто никто не возьмется. Даже если забыть о необходимости перевода на него legacy-кода

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

    Си++ — это язык, задача которого в том, чтобы знающий его мог твёрдо сказать «я знаю программирование». Проблема лишь в том, что за последние 25 лет требования к такому ответу повысились неописуемо.

    Но, тем не менее, он всегда будет, потому что ниша свободна, а занимать ее никто не торопится. Конкурентов нет и не предвидится. Так что будем его продолжать любить и учить. Хотя бы в ВУЗе. И будем благодарны за это, как мы благодарны Intel-у за x86. Дарёному коню в зубы не смотрят ;)
    • +3
      Даже если забыть о необходимости перевода на него legacy-кода

      Даже если сделать «новые плюсы», всё упрётся именно в поддержку старого кода. Не получится про неё забыть, это критический фактор (плюсы не взлетели бы без наследования си).
    • –4
      Да ладно? Так уж и единственный? Напомню, что и на Java писались игрушки (типа там Jake2) и операционные системы. А сейчас Mozilla сделала Rust. Он пока в alpha-стадии, но уже, с корявым генератором кода, уделывает Си++ по производительности благодаря более точной системе типов.
      • +2
        Пруфы?
        • –3
          Google: Jake2, JNode, Rust performance
          • +3
            Не этично. Но допустим. Однако выдается 4 ссылки и все мусор.Можно конкретную ссылку где описана конкретная методика и проведены результаты.
            • –1
              Странно. Я в Google отправил каждый из запросов, первые же ссылки — это то, что требуется. Ну ладно… Расшифрую.

              • bytonic.de/html/benchmarks.html — Jake2. Реализация Quake2 на Java. Бенчмарки можно посмотреть в таблице и самостоятельно проверить. Вебстарт до сих пор работает. Вроде как, на мусор ссылка не похожа
              • JNode доступна вместе с исходниками через сайт jnode.org. Странно его называть мусором
              • О производительности Rust можно почитать тут: pcwalton.github.io/blog/2013/04/18/performance-of-sequential-rust-programs Методика описана в этом же посте. Запускались просто программки из benchmarksgame. В некоторых задачах Rust уделывает Си++. Вроде, эта ссылка тоже не мусор

              • 0
                Вы послали в гугл, я вбил, что вы просили — выдался мусор.
                *Это как-то связано с Rust — по-моему нет. Это раз. Два, из приведенных значений видно, что более старый движок, который не ставил целью превзойти кого-то уделывает более поздние реализации.
                *Просто прелестно, и где смотреть бенчмарки
                *Последняя ссылка отсылает на benchmarksgame.alioth.debian.org/, но там Rust-а нет. В самой статье не сказано что с чем сравнивается. Если вы посмотрите в оригинале одна и тажа задача, решенная по-разному могут дать разные результаты. Выяснять истину охоты нет
                • 0
                  1. Это связано с Java. Вроде как, Вы просили указать Вам ссылки подтверждающие мои слова. Я говорил и о Java тоже. Вообще-то этой самой реализации уже лет 10. Не такая уж она и новая. Просто, обычно, утверждается, что вот Си++ единственный выбор для высокой производительности. Но получается, так-то, что на Java можно сделать не хуже. Я не фанат Java (да и вообще не фанат). Но просто эти примеры показывают, что утверждение об исключительности Си++ для высокопроизводительных приложений не соответствует истине. Можете ещё на технические отчёты IBM о NINJA посмотреть. www.cs.cmu.edu/~artigas/papers/cacm01.pdf

                  2. JNode — это операционная система на Java. Не очень понял, о каких бенчмарках идёт речь? Я лишь продемонстрировал некорректность утверждения о том, что Си++ — единственный подходящий для разработки операционных систем язык.

                  3. Там всё сказано. Имена программ однозначно задают исходные тексты, которые использовались для Си/Си++. Можете посмотреть на сайте, на который Вы ссылаетесь. Исходники соответствующих программ на Rust есть в хранилище его исходников. Почему его убрали из benchmarksgame я не особо в курсе. Но я перезапускал тесты, результаты показаны верные.

                  Ну… Это… Истина же не поменяется от Вашего желания её выяснять. А истина такова, что Си++ не такой уж единственный и неповторимый.
                  • 0
                    Моя вина, я говорил только про Rust.

                    Так вот на той странице указана сравнивалась самая быстрая реализация без SIMD и потоков. Так вот, какая? Ну и сравнивать быстродействие реализаций на O2 — некорректно. В этом случае большинство оптимизаций просто не работает.

                    То, что на всех остальных языках можно писать быстро никто не спорит. Однако на сях это делать гораздо проще. Шансов выстрелить себе в ногу (в плане производительности) во всех прочих (таких как Java, C# и т.л) гораздо больше
                    P.S про Jake. Но исходному движку на 10 лет больше!
      • +4
        Я очень сомневаюсь, что Rust уделывает (и вообще будет уделывать) C++ на каких-либо синтетических тестах-числодробилках. Что более реально — Rust будет уделывать C++ по скорости/лёгкости разработки. Если при этом он будет сравнимым по производительности, то свою нишу, которую сейчас занимает C++, он точно отгрызёт. И это хорошо.
        • –2
          Так вот именно, что уделывает. Повторюсь: pcwalton.github.io/blog/2013/04/18/performance-of-sequential-rust-programs/ У Rust более точная система типов, чем у Си++. За счёт этого можно местами генерировать более оптимальный код. При чём именно в вычислительных тестовых задачах.

          Вопрос в том, как эта система будет работать для реальных сложных задач. Но, пока, вроде, получается делать сложные вещи.
          • 0
            These benchmarks are showing their age quite heavily, they are too small and simplistic to extrapolate to real-world use cases, and many of them are too I/O-bound.
            Извините, парни, возможно, мы протестировали что-то не то.
            so the Rust versions of these benchmarks heavily use unsafe code.
            Фишка Rust — это безопасный код, в котором многие потенциальные ошибки отсеиваются в момент компиляции. Я не вижу особого смысла тестировать unsafe Rust: мы лишаемся большого преимущества языка, взамен получая некую скорость… Но для такого у нас уже есть C++, зачем тогда вообще писать на Rust?

            Итого:
            The goal here is simply to demonstrate that sequential Rust can be written in a way that approaches competitive parity with equivalent C code.
            То есть разогнать Rust «неканоничным» для языка способом видимо действительно можно. Только зачем?
            • 0
              github.com/rust-lang/rust/blob/master/src/test/bench/shootout-mandelbrot.rs — на 30% быстрее, чем Си++ вариант. Unsafe-кода, вроде как, тут нет. Речь же была именно о вычислительных задачах. В задачах, где надо заниматься активно работой с памятью, да, им приходится использовать unsafe. Во многих других бенчмарках тоже ничего небезопасного нет. Всё же выложено. Можно почитать, прежде чем делать утверждения.
              • 0
                Ну это вы что-то намешали мух с котлетами. Бенч, который использовался по вашей ссылке, насколько я могу судить, лежит здесь: небезопасный, однопоточный, без SIMD. Теперь вы показываете другой бенч — вроде да, безопасный (хотя с feature gates, но стабильного релиза языка ещё не было, так что ладно), многопоточный, с SIMD. А где посмотреть сравнение с таким же бенчем на C++?
      • +1
        Игрушки на Java, кнечно, пишутся — для того она и есть. А вот ядро системы чтобы было написано на Java или драйвер — вот такого не встречал…
        • 0
          Да их много таких систем. JNode (самый живой проект на настоящий момент), JX, JavaOS, JOS. Есть ещё современный интересный облачный проект OSv — там не чистая Java, но всё равно занятно.
          • 0
            Будем посмотреть… Не очень пока понятно, как на языке, исполняющем код в песочнице, можно написать, например, драйвер устройства. Думаю, какой-то простейший Си там, всё же, есть…
    • +1
      > Проблема в том, что чтобы сделать «новый C++», несовместимый со старым (и, благодаря этому, лишенным всех его недостатков), требуется слишком много сил и времени. За это просто никто не возьмется.

      Взялись и сделали. D. Но мало сделать язык лучше C++, нужно сделать язык для замены C++, а тут в дело вступают бизнес-интересы. Как и любому продукту, языку нужна раскрутка и пиар. Поэтому Go на слуху, а D нет.
      • 0
        Ну так я включаю сюда еще и пиар, разумеется.
  • +2
    Препятствием может быть неизвестная предметная область, высокие требования по быстродействию или потребляемой памяти, необходимость общаться со странным чужим кодом или аппаратными устройствами, неопределённость задач и т.д.

    Если препятствиями выступают жёсткие технические требования (скорость, память, размер, переносимость) — си/плюсы рулят, вопросов нет. Конкуренция отсутствует.

    Вопросы есть к пунктам про проблемы с описанием предметной области и задач. Тут-то C++ как помогает? Эти требования скорее динамически типизированными языками и прочими скриптами попахивают. Или, как минимум, упрощениями жизни типа сборки мусора. Меньше кода и возможностей отстрелить ногу — больше времени на эксперименты, прототипирование, рефакторинг, переделвание по десять раз и т.п.
  • +5
    Периодически возвращаясь к любимому С++ всякий раз поражаюсь, насколько все-таки медленно на нем писать.
    Да, отличный оптимальный код. Да, неплохое сокрытие деталей. Перегрузки, шаблоны, все классно — но оооочень медленно пишется. Впечатление неизменно и не меняется много лет. Не только сам, аналогичное наблюдал и с другими людьми. Пришел — давно — к выводу: писать на C++ — только когда совсем прижмет: скорость, объем памяти и т.д.
    • 0
      насколько все-таки медленно на нем писать
      Same here. Язык развивается на глазах, C++11/14/17 приносят вагон вкусняшек, но именно вот это ощущение медленности не покидает. И что важно — во многих случаях особых альтернатив нет. D на распутье, вроде бы ему 13 лет, но язык не видно и не слышно. Может быть, Rust выстрелит?
    • –9
      Вы путаете, «медленно быдлокодить»… вы наверное так хотели сказать? Просто в задачи решенные ( решаемые ) на С++/С значительно превосходят по своей сложности всякие задачи на других языках. Кто бы как не спорил, но другого такого языка никто не придумал еще. А если и придумает, то он должен с легкостью конвертировать, переделывать, перелопачивать все решенные задачи, либо поддерживать существующие решения. Т.е. C++11/14/17 это и есть будущее. Ну все фундаментальное на нем сидит уже. Не сделаешь ты ffmpeg на другом языке, да и зачем, если уже есть. А вообще будущее именно за разделением языков. С++ как основа… а остальное — прикладное. Вот прекрасные примеры С++ / QML… С++/Lua… С++ / Unity 3d… Неужели не ясно, что этот язык нужен для фундаментальных нужд? Все остальные — лишь прислужники этому титану… (не будем говорить про ASM = )) Вот за этим подходом и будущее. Гоблины в подземельях лабают на плюсах, а всякие верховные колдуны уже хреначат на явах, сишарпах, питонах и т.п. Но только все лавры конечно достаются им, а не бедным гоблинам.
      • +11
        я имею в виду именно писать — начать и сделать работающую неглючную программу, внести новую фичу в ранее написанную и добиться, чтобы фича работала как надо.

        Я могу навскидку назвать несколько причин, которые делают кодирование на C++ долгим. Ни одна из них не связана с «быдлокодом».

        Первая по видимости но не по значимости — время компиляции. Полчаса на перекомпиляцию проекта — обычное дело. Это частично решается makefile и объектами прекомпиляции заголовков, но если вы отлаживаете многократно используемый заголовок — мало помогает.

        Вторая причина, менее заметная, но более значимая — ошибки, которые я для себя называю «дальнодействующими», связанные с порчей памяти тем или иным способом. Дальнодействующие — потому что причиной порчи может быть абсолютно любой код, как принадлежащий вам, так и состоящий в используемой библиотеке. Ошибка в коде на языке, который не может обращаться к памяти, как правило приводит к неработоспособности именно того участка кода, где находится ошибка, ну или кода, который обращается к неверному участку. Обычным делом для C++ является ситуация, когда вы видите, что данные испорчены — имеют невозможное состояние — но не представляете себе, кто — какой код — залез в эти данные и накосячил. Самое поганое — такая ошибка в сложной программе может быть многажды отражена, когда плохой код портит один участок, а нормальный код, опираясь на испорченные данные, начинает портить другой участок — и так много раз. В результате вы можете получить segfault из кода, который был протестирован и отлажен и начинаете гадать, как он мог быть испорчен. Из более чем 20 лет программирования я лет 5 в сумме, а может и больше, потратил именно на ловлю таких ошибок — как у себя, так и в чужом коде. Один проект зафейлился из за того, что источник ошибки так и не был найден после того, как на его поиск было потрачено суммарно больше времени, чем на разработку и отладку всего остального.

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

        Еще один источник затягивания сроков готовности — потери памяти. В не очень сложных случаях помогают готовые инструменты, но основной способ борьбы — дисциплина кода, что разумеется, также отнимает время.
        • 0
          Аплодирую вам стоя! Вы просто сформулировали абсолютно все причины, по которым я вместо C++ предпочитаю использовать связку Java + C (где C используется через JNI для «острых» мест)
    • 0
      Еще из часто посещаемых мыслей:

      — «боже, какой ужас творится у меня в коде, когда я все это уже перепишу нормально, (3,5,10… подставь свою цифру) лет уж прошло, надо все таки все переписать, вот и Сх11 подоспел (начинаешь потихоньку использовать auto...). Еще надо бы попробвать to_string, интересно как оно по скорости будет… И еще поток мыслей. Вообщем постоянно что-то менять приходиться.

      где-то на задворках сознания постоянно мелькает:
      — »А может на чистом С все переписать, выкинуть тормознутый STL?", и тут же отметаешь эту мысль…
  • +3
    Почему вы не ставите вопрос «почему С не утонул?». Язык и древнее, и топорней. И, судя по графикам, популярней.
    • +3
      Си будет существовать до тех пор, пока компьютеры будут исполнять программы так, как они их исполняют — команда за командой, инструкция за инструкцией. И до тех пор, пока будет существующая модель памяти.

      Потому что Си — «кроссплатформенный ассемблер». Это язык самого низкого уровня, при котором от разработчика еще не требуется вникать в архитектуру процессора. Написанное на Си будет работать везде, вне зависимости от того, что у вас за железо. Это обосновывает необходимость его существования железно. Раз и навсегда.
  • 0
    Мне не понятно, почему на D так вяло переходят. У меня два приятеля перешло. Вроде пока довольны. Один с С++, другой начал использовать D как второй язык (основной C#).

    Остальные как писали на С++, так и чертыхаясь продолжают писать.

    Где логика? Ладно бы там С++ --> Lisp был бы, там действительно сложно перейти, а тут все очень просто, но инерция колоссальная.
    • +7
      Логика простая:
      C++
      1. Кодовая база > 1000000 LOC. Компания инвестировала большие деньги в этот код на протяжении многих лет, чтобы его переписывать
      2. Предметная область более сложная чем C++, и больше влияет на сроки сложность обусловленная не языком
      3. Активно используются сторонние C/C++ библиотеки
      4. В компании много экспертов в предметной области хорошо знающих C++, и не знающих D.
      5. На рынке много кандидатов знающих C++

      D:
      1. Нет веры в стабильность языка. Код на D, написанный 10 лет назад сейчас видимо уже нерабочий. Более старый код на C++ прекрасно работает до сих пор. Где гарантия, что код написанный на D сейчас, будет работать через 5 лет?
      2. После появления C++11 я не вижу killer feature D
    • +2
      И бросить все наработки свои и коммерческие на С++? И удивится что D нет даже утвержденной библиотеки поставки? Для каких платформ есть инструментарий — хотя-бы компилятор, про удобства типа IDE/Debugger/Статический анализатор — промолчу? Без сильного коммерческого покровительства со стороны софтверных гигантов прорыва ждать не приходится.
    • 0
      Потому что D — это просто ещё одна попытка переизобрести C++
  • +3
    С++ превосходный язык, но пользоваться им проблематично. Но основная проблема — это не несовершенство языка, а менталитет программистов на нём. В Java для каждого действия написана библиотека, красиво упакована и положена в централизованное место. Сама библиотека полная и каждый класс имеет кучу перегруженных функций на каждый чих: надо преобразовать массив байтов в строку в Base64 — пожалуйста. А что на крестах? Каждый пишет в своём проекте свою функцию «tobase64(char*)» и там же она и остаётся. Из цельных framework'ов в голову приходит только Qt. Вроде функции маааленькие, простенькие и тянуть что-то не хочется, но из состояния потока выбивает, когда приходится задумываться не о том, что надо сделать, а о том, как.
    • +4
      Boost не забывайте.
      • 0
        Поддерживаю, вот честно что бы написать что угодно на C++ не хватает только std::gui или boost::gui и тогда я искренне могу сказать, что связка С++ STL и Boost покрывает абсолютно все общие задачи и останется подключить (или написать если область новая) только узкоспециализированную либу для предметной области и всё.
        • 0
          есть GUI: ncurses или OpenGL, на выбор ))
          • 0
            Да, это так, есть ещё много чего тот же Qt, WxWidgets и т.д.. Но я просто постарался описать самый минимальный набор либ, т.е. если перефразировать мой комментарий: сейчас на C++ связка STL + Boost уже покрывают абсолютно всё кроме GUI и чего то совсем узкоспециализированного :)

            p.s: OpenGL это всё таки графика а не gui, а ncurses вообще консольный гуй и сишный )
            • 0
              На то я и взял крайности, чтобы показать, что кроссплатформенные либы покрывают все потребности.
        • 0
          а Qt вам чем ко двору не пришелся?
          • +1
            Qt хорош, я лишь говорю какой раздел надо добавить в STL или в Boost что бы только ими обходиться. Единственный, более менее значимый, минус Qt это десятки мегабайт либ, которые надо с собой таскать.
            • 0
              Можно обходиться одним только Qt в таком случае (не могу навскидку сказать чего в нем нет, что есть в STL+Boost)… И таскать только те либы, которые используете (как и в случае с Boost).
              • +1
                Вы правы конечно можно, просто ветка о другом и мой комментарий о другом.

                Ветка начинается с комментария, что в C++ много чего не хватает «из коробки», вторым комментарием идёт верное замечание о том, что забыли о Boost. Третьим идёт мой комментарий о том, что сейчас для всего кроме GUI в C++ хватает STL, т.е стандартной либы и Boost, дефакто являющейся её продолжением. Т.е. С++ сейчас покрывает «из коробки» все потребности программиста, но для гуй всё же требуется подключить third-party-lib ибо классов для работы с ним нет ни в Boost, ни в STL, а какую либу подключить уже не важно.

                Что касается Qt: даже частичное использование Qt приводит к тому что значительная его часть тащится в проект, происходит это поскольку Qt это очень мощный универсальный фреймворк для всего в основе которого лежат свои архитектурные решения а в производных классах используется глубокое наследование и естественно следование общей архитектуре фреймворка. Только по этой причине и появляется необходимость таскать много метров библиотек, поскольку подключение Qt только для GUI, а в контексте ветки имеет значение именно такое использование, приводит к тому, что большая часть Qt постепенно оказывается в проекте ибо GUI компоненты без многих частей фреймворка просто не работают, вот о чём шла речь.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка