Пользователь
68,7
рейтинг
12 апреля 2011 в 11:01

Разработка → Очнитесь, на дворе XXI век tutorial

C++*


Начать статью я хотел бы с констатации того факта, что прямо за окном находится 2011 год (пруфлинк), середина апреля. Напоминаю я это в первую очередь себе, поскольку меня периодически посещают в этом сомнения. Дело в том, что как по работе, так и ради хобби я часто читаю код на С++, написанный лет 10-20 назад (но поддерживаемый и поныне) или код написанный совсем недавно, но людьми, которые учились программировать на С++ те же 20 лет назад. И вот после этого у меня и возникает ощущение, что никакого прогресса за эти годы не было, ничего не менялось и не развивалось, а по Земле до сих пор бродят мамонты.


Вступление


Из КВН:
-А где тут у вас в Сочи, бабушка, можно комнатку снять долларов за 25 в день?
-А, так тут недалеко, ребятки. В 90-ом году.


Специфика программирования 20 лет назад была совсем другой. Счет памяти и ресурсов процессора шел на байты и такты, многие вещи еще не были изобретены и приходилось выкручиваться. Но это вовсе не повод и сегодня писать код исходя из этих предпосылок. Мир меняется. Я чувствую это в воде. Я чувствую это в земле. Вот, уже и в воздухе этим запахло… Нужно не отставать.

Все, что я буду дальше писать касается только программирования на С++ и только mainstream-компиляторов (gcc, Intel, Microsoft) — с другими языками и компиляторами я работал меньше и говорить о положении вещей в них не могу. Также я буду говорить только о прикладном программировании под десктоп-операционки (в кластерах, микропроцессорах и системном программировании тенденции могут отличаться).

TR1


Для тех, кто был в танке последний пяток лет я расскажу великую военную тайну (токо тссс!). Есть такая штука, как TR1. Это может стать откровением, но почти во всех современных компиляторах есть встроенные умные указатели, неплохие генераторы случайных чисел, много специальных математических функций, поддержка регулярных выражений и другие интересные вещи. Вполне неплохо работает. Пользуйтесь.

C++0x


Для тех, кто приобщился к кружку сидения в тяжелой бронетехнике всего пару лет назад я сообщу еще одну благую весть. Есть такая штука, как C++0x. Возрадуйтесь, братья! Да, официально на нём еще не стоят несколько высоких подписей и церемония разбития бутылки шампанского о борт стандарта еще не состоялась, но релиз-кандидат утвержден и поддержка в компиляторах уже есть.

Уже сейчас к Вашим услугам:
  • Лямбда-выражения
  • Rvalue ссылки
  • Обобщённые константные выражения
  • Внешние шаблоны
  • Списки инициализации
  • For-цикл по коллекции
  • Улучшение конструкторов объектов
  • nullptr
  • Локальные и безымянные типы в качестве аргументов шаблонов
  • Явные преобразования операторов
  • Символы и строки в Юникоде
  • «Сырые» строки (Raw string literals)
  • Статическая диагностика
  • Template typedefs
  • Ключевое слово auto

и куча других полезных вещей.
Ну вот посмотрите хотя бы на следующие примеры:

  • Вместо
    vector<int>::const_iterator itr = myvec.begin();
    теперь можно написать
    auto itr = myvec.begin();
    — и это будет работать! Более того, даже строгая типизация никуда не девается (auto — это не указатель и не Variant, это просто синтаксический сахар)
  • Теперь можно ходить по коллекциях аналогом цикла for_each

    int my_array[5] = {1, 2, 3, 4, 5};
    for(int &x : my_array)
      x *= 2;


Ну красота же, правда? Напомню, это поддерживается в основных, стабильных (не альфа\бета) ветках всех основных компиляторов. И это работает. Почему Вы этого до сих пор не используете?

Передача всего и везде по указателям (ссылкам)


Возможность передавать сущности в функции и методы как по ссылке так и по значению — очень мощный механизм и не стоит его использовать однобоко. Часто я вижу, как по указателю передаётся вообще все и всегда. Аргументы у людей такие:
  • Указатель передаётся быстрее, чем структура данных — прирост скорости.
  • При передаче по указателю нет нужны в дополнительной копии — экономия памяти.

Оба аргумента несущественны. Выигрыш часто составляет пару байт и тактов (его даже не получается экспериментально измерить), но вылазит целая куча недостатков:
  • Функция-приемник вынуждена проверять все аргументы как минимум на NULL. Да и тот факт, что указатель не NULL тоже еще ничего не гарантирует.
  • Функция-приемник вправе сделать с передаваемой сущностью все, что угодно. Изменить, удалить — всё. Аргумент о ключевых словах «const» — не аргумент. В С++ масса хаков, дающих возможность изменить данные по константному указателю или ссылке.
  • Вызывающая функция вынуждена либо доверять вызываемой в части изменения данных, либо валидировать их после каждого вызова.
  • Значительная часть объектов, передачу которых пытаются оптимизировать использованием указателей сами по себе являются почти чистыми указателями. Это касается как минимум классов строк, работа с которыми оптимизировано везде и давно.

Я приведу аналогию: у Вас дома вечеринка, присутствует десяток хороших друзей + пару случайных личностей (как всегда). Вдруг одна из таких личностей замечает на Вашем компьютере забористое порно познавательный фильм о природе и просит дать посмотреть. А Вы вместо того, чтобы записать кино на флешку\DVD выключаете компьютер из розетки и отдаёте со словами: «На, забирай — смотри». Ну чушь ведь, правда? Так почему Вы в коде отдаёте все свои данные на поругание какой-то непонятной функции, которую вообще не пойми кто писал.

Вычисление констант


Вот кусочек кода:
#define PI 	3.1415926535897932384626433832795
#define PI_DIV_BY_2 	1.5707963267948966192313216916398
#define PI_DIV_BY_4 	0.78539816339744830961566084581988
...
< еще 180 аналогичных дефайнов >
...


Может быть для кого-то я открою великую тайну, но константы нынче вычисляются компилятором при компиляции, а не на рантайме. Так что «PI/2» будет читаться легче, места занимать меньше, а работать так же быстро, как и 180 дефайнов в примере выше. Не недооценивайте компилятор.

Собственные велосипеды


Иногда я вижу в коде что-то типа:

class MySuperVector 
{ // моя очень быстрая реализация вектора
...
}


В этот момент меня пронимает дрожь. Существуют STL, Boost (и многие другие библиотеки), в которых лучшие умы планеты уже который десяток лет совершенствуют множество прекрасных алгоритмов и структур данных. Писать что-то своё стоит только в 3-ех случаях:
  • Вы учитесь(лабораторная, курсовая)
  • Вы пишете серьёзную научную работу именно на эту тему
  • Вы наизусть знаете коды STL, Boost, десятка аналогичных библиотек, 3 томика Кнута и четко уверены, что решения для Вашего случая в них нет.

В реальности происходит следующее:
  • Люди понятия не имеют о наличии библиотек
  • Люди нифига не читают умные книги
  • Люди имеют завышенную самооценку, считают себя умнее всех
  • «Чукча не читатель, чукча — писатель»

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

Ненужные оптимизации


Пример:
int a = 10;
a <<= 1;


Что тут написано? Я верю в читателей Хабра и думаю почти все знают, что это побитовый сдвиг. И многие знают еще и о том, что эти операции для целых чисел эквивалентны умножению и делению на 2. Так было модно писать раньше, поскольку операция побитового сдвига выполняется быстрее, чем операции умножения и деления. Но вот они факты на сегодня:
  1. Все компиляторы достаточно умны, чтобы самостоятельно заменять умножение и деление на сдвиг в подобных случаях.
  2. Не все люди достоточно умны, чтобы понимать этот код.

В результате Вы получите хуже читаемый код, без преимуществ в скорости работы и периодические (в зависимости от количества и квалификации коллег) вопросы: «А че за хрень?». Зачем это Вам?

Ненужная экономия памяти


Я дам пару ссылок на случаи, когда люди пытались сэкономить 1-2 байта памяти и что из этого вышло.

Уже сегодня у нас есть в среднем от 2 до 4 Гб ОЗУ. Еще пару лет и все вокруг будет 64-битное и памяти будет еще больше. Думайте наперед. Экономьте мегабайты, а не отдельные биты. Если речь идет о количестве людей, предметов, транзакций, температуре, дате, расстоянии, размерах файлов и т.д. — пользуйтесь типами long, longlong или чем-то специализированным. Забудьте о byte, short и int. Это всего-лишь несколько байт, а переполнение в будущем может стоить очень дорого. Не поленитесь завести отдельные переменные для разных сущностей, а не использовать одну временную с мыслью «а, все равно они никогда одновременно использоваться не будут».

Выводы


Не программируйте наскальную живопись. Потомки не оценят.
Владимир @tangro
карма
710,2
рейтинг 68,7
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

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

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

  • +96
    Хорошие заметки.

    Преждевременная оптимизация хуже преждевременной эякуляции
    • +10
      Ну у вас и приоритеты. Лично я бы предпочёл наличие первой вещи, чем второй. :) Да и ваша категоричность меня удивляет. Каждая задача решается своим набором инструментов и подходов. У меня в практике были такие задачи, что я очень был рад проделанной предварительной оптимизации, т.к. она реально решала потом кучу проблем (realtime кодирование аудио и видео потоков, работа с графикой). Если у вас не было такого опыта в жизни, то ваше такое критичное утверждение очень малоценно. :)
      • 0
        Ну я же говорил о преждевременной…

        Тем более узким местом обычно является не так много кода, там можно приложить все силы
        • +5
          И я о преждевременной :) Когда я работал над проектом по реалтайм сжатию видеопотоков — там ОЧЕНЬ многое оптимизировалось ДО имплементации в коде. Выбирались корректные размеры структур, размещение данных (кадров) в памяти, организация памяти, распределение по потокам и имплементация быстрой синхронизации, определялся набор разрешённых ассемблерных инструкций (был опыт когда работа с float значениями на уровне С++ ломала конвейер процессора и fps падали в 2-3 раза) и даже выбирали язык программирования. Только после этого приступали к реализации. Результаты таких преждевременных опримизаций — впечатляли.
          Да и вообще для меня, количество категоричных заявлений программистом обратно пропорционально его опыту — с опытом приходит понимание, что задачи бывают ну очень разные. Не будьте так категоричны ;)
          • +2
            мне вообще фраза нравится.

            «преждевременная» оптимизация должна быть на бумаге, и быть результатом целенаправленного труда, а не побочным эффектом
          • 0
            Имеется в виду один из постулатов Суттера — а именно, что большинство программистов на плюсах не до конца понимают, как это ни печально, что именно они делают и поэтому они как бы «оптимизируют» код, а на самом деле выигрыш в скорости бывает нулевым, если не отрицательным
          • +1
            Я думаю, что тут некоторая путаница в понятиях «преждевременный» и «ранний». Последнее бывает как и своевременным, так и преждевременным. Как-то вот так.
    • +2
      Я смотрю, у Вас это стало коронной фразой. Подозреваю, ещё не раз её увижу в комментах с Вашим авторством.
    • +2
      Преждевременная пессимизация хуже импотенции

      final fix
    • 0
      Снимаю шляпу перед вами — хорошие слова, еще Фаулер говорил что если проект хорошо спроектирован то он легко поддается оптимизации — но только после того как будет проведено профилирование и будут известны узкие места — а то нынче оптимизация ради оптимизации и вообще это модно)
      • –1
        я тоже в школе этим болел. даже делала так:
        char str[10];
        char *p = str;
        ...
        str++;
        ...
        if( *p == ' ' )
        ...

  • +5
    Я просто скажу, что в телефонах отнюдь не 2-4 Гб памяти, пока )
    • +8
      В начале статьи я отдельно отметил, что речь идет о программировании под десктоп-операционки.
      • +1
        Ок )
      • +17
        Часто приходится под всё сразу программировать, поэтому некоторые фичи сразу отметаются, а на мобилах еще часто и компиляторы на наскальную живопись похожи.
      • 0
        Только хотел задать вопрос о кросскомпиляции, как проверить правильную работоспособность…
        Уже не буду.
        Грустно.
    • +1
      Ага, и даже на терминалах (сбора данных) пока, к сожалению. Бывают и stack overflow, и low memory. А по сети передавать с них данные — отдельная сказка.
    • +1
      По-мойму для телефонов через год это уже мало будет. Глядя на нынешние телефоны, которые уже покруче старых компов будут.
      • +1
        Вы посмотрите на apple. Они каждый Вт*ч экономят. У них не более 0.5 Гб
      • +4
        Может и покруче, но некоторые демонстрации различных эффектов, которые я наблюдал еще на 286х, почему-то круто тормозят на телефонах «покруче». Процессор явно сильнее, видеокарты тоже, а тормоза, словно запустил Descent на 486 DX2 66…
        • 0
          причина — экономия на памяти
          а про Descent на 486 DX2 66 правильно подмечено.
          • +2
            Может причина в отсутствии оптимизаций как таковых? Рост объемов памяти, скорости процессоров, видеокарт, шины передачи данных развратило рынок. С одной стороны это неплохо, забыв про ограничения люди наконец начали создавать продукты, которые работают уже сейчас, и не нужно ждать пол года, чтобы получить работающую версию. С другой стороны аналогичные подходы к разработке неприменимы в условиях ограниченных ресурсов. В таких условиях оптимизация не просто играет роль, она жизненно необходима с самого начала.
            Лично меня удивляет, как на телефонах тормозит слайдинг одного рабочего стола на другой. Для этой операции хватит мощности 286-го компьютера даже без участия видеокарты. Откуда такие тормоза?
            • 0
              очевидно из-за не оптимальных библиотек
              я — разработчик WEb-приложений под высокие нагрузки.
              стараемся меньше использовть сторонних библиотек
              • 0
                Ну так переписать библиотеки надо. Да, я понимаю, что звучит нагло, но это тому же Гуглу будет дешевле.
            • 0
              я при оптимизациях и поиском узких мест под Symbian OS очень удивился, что процентов эдак 90-95 процессорного времени тратилось на ожидание устройства ввода/вывода. постоянная память на телефонах очень медленная, это, бывает, не учитывают
              • 0
                Архитектурная ошибка?
                • 0
                  Я, честно, не ожидал, что seek в файле + запись в его конец (точнее перетирая с конца ~2 байтов + за пределы файла) ~100 байт будет отрабатывать 200-300 мс вместо 1-2 мс на десктопе
                  • 0
                    Мдааа
    • 0
      ну да, всего лишь 256Мб-1Гб.
    • 0
      Да все к тому идет. Пару ядер и несколько гиг памяти — даже в брелке.
    • 0
      Теперь уже и 2, и 4:)
  • НЛО прилетело и опубликовало эту надпись здесь
    • +5
      Ко всему прочему, C++ настолько красив, что позволяет оптимизацию сложить на плечи компилятора (vector например).

      А уж если делать наскальную живопись, то надо ее располагать в одном месте, а не размазывать по всему коду
    • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      s/1/100500/g
  • +14
    столько вкусного, но я на С пишу :(
    • +47
      У меня друг от этого умер…
      • –3
        у меня — брат
    • 0
      Когда нужно добиться максимальной производительности, тоже пишу на C, плюсам не доверяю) Мне почему-то кажется, что многие фичи C++ тормозят программу (если сравнивать с аналогичной на C), хотя многие говорят, что это миф. А так, если производительность не нужна, то и мучится с C смысла нет.
      • +4
        Да. Это миф. Не многие, а только определенные.
      • +1
        это не миф, но таких задач крайне мало
      • +3
        мы делали замеры: Использование fwrite/fread на 20% производительнее iostreams
        • +1
          а время на форматирование строки учитывали?
          • –1
            Вы путаете с fprintf :)
            • +1
              а вы бинарные данные писали?
              • 0
                А что ещё можно писать при помощи fwrite/fread и iostream? Man бы хоть прочли.
                • 0
                  iostream у нас перестал поддерживать operator <<?
                  • 0
                    Ну… Я имел в виду iostream.{read,write}, явно же с ними сравнивали прозиводительность. Иначе, не разумно.
                    • 0
                      тогда интересно было бы узнать причины… не пробовали понять?
                      • 0
                        Ну. Я пробовал. Но iostream (в libstdc++) написан настолько trueЪ-хардкорно, что я не осилил и забросил это дело. Но скорее всего, какие-то плюсовые накладные расходы. Может быть, даже на поддержку тех же исключений.
                        • +1
                          про расходы на поддержку исключений — это бред. особенно если код генерится gcc — там будет такой же перехват исключений
                          • 0
                            Исключения будут в fread/fwrite? Там их не написано.
                            • 0
                              ну вообще компилятор очень редко может предполагать это
        • +2
          Ну так fwrite/fread более низкоуровневая обёртка к системному вызову write/read (даже прототипы почти одинаковые), чем iostream, поэтому и работает быстрее.
  • +4
    XXI Охота на динозавров.
  • +19
    Какое чудесное у вас Пи:
    #define PI 3,1415926535897932384626433832795

    Не буду говорить, что получится, если сказать, например,
    x = PI/2;
    
    • +1
      :) скопировано из виндовс-калькулятора. Сейчас поправлю.
      • +1
        Когда-то сделал точно так же и полчаса баг искал :)
    • 0
      А в чем фишка?
      • +7
        запятая
        • +2
          Интересно, за что вам поставили минус…

          Разверну ваш ответ для тех, кто так и не понял: фишка в том, что разделителем целой и дробной части в всех [известных мне] языках программирования является точка, а не запятая. А макрос определён именно с запятой:

          #define PI 3,1415926535897932384626433832795

          Таким образом, при разворачивании макроса в выражении

          x = PI/2

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

          Очередной аргумент в пользу использования типизированых констант вместо макросов ;)
    • 0
  • +11
    Сказать Линусу Торвальдсу и другим ядерщикам, что они живут в каменном веке. :)
    Вот его известное письмо:
    thread.gmane.org/gmane.comp.version-control.git/57643/focus=57918

    Я со многим в этой статье согласен, да и сам плюсовик со стажем, но считаю, что на любом языке и любыми средствами можно написать плохой или хороший код.
    • +1
      У них есть свои причины.

      C++ хороший способ переложить на компилятор заботу о многих вещах, о которых вы вынужденные думать в С, без потери производительности.
      Вполне можно огриичиться перегрузкой, шаблонами, и не использовать runtime фишки (динамичскую полиморфность, RTTI).
      А те же исключения вообще замечательная вещь. И может быть реализованно с нулевой стоимостью для правильного потока исполнения (в С вы всегда должны проверить возвращаемое значение).
      • +2
        Раскрутка стэка по исключению и связанные с ней накладные расходы обходятся дороже, чем проверка возвращаемого значения (http://ddima.livejournal.com/67706.html, разница на синтетическом примере порядка 35%)
        • 0
          apple для obj-c исключений рапортовали о нулевой стоимости.

          Хорошая идея проверить это для gcc.

          ЗЫ: я имел ввиду нормальный путь исполнения, а не раскрутку стека
          • 0
            Надо будет посмотреть да.
            По поводу ЗЫ: дело в том, что, для того, чтобы раскрутить стек, нужно иметь соответствующий механизм, который требует ресурсов и не так уж и мало — пройдитесь по ссылке, посмотрите http://ddima.livejournal.com/67706.html
            • 0
              Что-то быстрый взгляд (а тут еще заказчик мозг имеет) не увидел стоимость использования исключения при нормальном исполнении кода
              • 0
                Сорри, попутал ссылку ;)
              • 0
              • +1
                • 0
                  Не настолько я крут в этом. Но заинтересовало. В связи с отсутствием винды, буду тестить clang/gcc. Но когда — не знаю.

                  Вообще, как мне кажется, в винде еще надо и системные исключения перехватывать (забыл как они зовутся, типа превышения стека, SEH вроде). И, мне почему-то, всегда казалось, что в винде есть необходимость именно докладывать данные на стек.
        • 0
          На самом деле не все так страшно. Главное помнить, что исключения должны быть именно искючениями. Что же касается тормозов в конкретных версиях компиляторов — это проблемы этих конкретных версий. Потенциально исключения, до тех пор пока они не вызваны, не должны замедлять работу программы в хорошо спроектированном компиляторе.
          • 0
            Ну. Как оказалось, до mac os x 10.5 компилятор obj-c использовать *jmp функи для перехвата исключений, и это замедляло (хоть и не сильно) нормальное течение процесса.
            Как сейчас сделано, не знаю. Но думаю, что уже весь mainstream давно научился не замедлять код. Ведь C++ всегда должен ожидать получения исключения — деструкторы должны отработать.
      • +3
        Нулевая стоимость использования исключений — миф. Наличие исключений может сильно ограничивать оптимизацию, даже если эти исключения никогда не срабатывают.
        • +1
          Интересно. А можно поподробнее?
          • +7
            По хорошему это статью надо писать про это. Если коротко, то границы try-catch региона являются препятствием для оптимизаций. Например, иногда нужно поднять загрузку как можно выше, чтобы к использованию данные были уже готовы, то граница try-catch региона может ограничить такую трансформацию. Оптимизации циклов, по-моему, вообще отрубаются в большинстве случаев, когда внутри циклов есть исключения. Так происходит потому, что практически любая трансформация цикла может изменить поведение исключений (допустим если мы сделаем fusion циклов, в одном из которых есть исключения, то результат будет совсем не предсказуем с точки зрения программиста). Что там происходит с аллокацией регистров точно не знаю, но подозреваю, что там тоже возникают свои «особенности».

            И даже когда компилятор теоретически способен провести трансформацию, наличие исключений делает доказательство валидности трансформации очень сложным делом и, естественно, разработчики компиялторов по возможности избегают лезть в эти дебри.

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

            Надеюсь я достаточно полно ответил на вопрос.
            • 0
              То есть ограничение накладывает не сама возможность исключений, а наличие блока перехвата их. Тогда да, полностью с вами согласен. Как минимум необходима отработка деструтора при выходе из блока try.

              Но 99% кода не перехватывают исключение. В этом случае допустима полная оптимизация. И этот код от исключений только выигрывает.
              • +1
                Не совсем верно. Наличие try-catch накладывает больше ограничений, нежели возбуждение исключений. Но возбуждение исключений тоже препятствует оптимизации. Хотя, по сути, исключение эквивалентно обычному вызову неизвестной компилятору функции, который точно также препятствует оптимизации (не все об этом задумываются). В этом случае возможность исключения в цикле предполагает возможность «незапланированного» выхода из цикла и это, например, может помешать параллелизации цикла или фьюжену с соседним «безопасным» циклом.

                На счёт деструктора я не совсем понял вашу мысль.

                Оговорюсь на всякий случай, что я говорю о синхронной модели исключений, т.е. обычные плюсовые исключения, которые бросаются throw и никак иначе.
                • 0
                  я имел в виду, что при вызове любой функции компилятор должен ожидать, что из нее вылететь исключение (исключая те функции, которые он может подставить).

                  Таким образом всегда должна быть возможность обработать раскрутку стека
                  • +1
                    Это верно. Именно поэтому не стоит звать в продакшн коде в хотспоте всякие дебаг функции, которые тем более ещё не видны компилятору :)

                    Кстати, компилятор распознаёт многие вызовы к стандартной библиотеке и считает их безопасными (если они действительно безопасные, конечно).
                    • 0
                      Чаще всего ты вызываешь свой код. А он не безопасен по его мнение

                      А throw() накладывает другие ограничения и может быть недешевым (хотя как сейчас, не знаю)
                      • +1
                        Всё зависит от того где определять свой код и как компилировать. Если код определён в том же объектнике, то компилятор должен понять насколько безопасен этот код (интеловский компилятор это делает, за другие не поручусь, но должны), а может быть даже его просто проинлайнит.

                        Если же код определён в другом модуле, то может спасти межпроцедурная оптимизация. В интеловском компиляторе это ключик -ipo, у gcc начиная с 4.5 это LTO (link time optimization, какой ключик не знаю).
                        • 0
                          Это да. Но все это имеет смысл для часто вызываемых малых функций.

                          В любом случае, если вы в массиве обрабатываете чтения из 100 файлов, то оптимизация вас не спасет.

                          Но общая идея понятна.
        • 0
          +1
          Почитал комменты, и вижу, что мало кто задумывается, как можно реализовать все многообразие функциональности исключений «с нулевой стоимостью».
          Почитайте, например, как сообщество IA64 предлагает реализовывать исключения:
          www.codesourcery.com/public/cxx-abi/abi-eh.html
          Особенно обратите внимание на «Runtime initialization», и пр. «мелкие нужды».
          • 0
            По ссылке просто ABI, не совсем понял как это относится к делу. На x86 всё тоже самое по сути. Если вы про то, что на самом деле там куча всяких ньюансов в технической реализации и сама реализация включает в себя кучу всяких таблиц и дополнительной информации в объектниках — это да.
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Лучше? Чем? затратами при нормальном исполнении?
          • НЛО прилетело и опубликовало эту надпись здесь
            • +2
              Слова «штатная» и «исключение» являются скорее антонимами.
              Использование исключений во время штатных ситуаций — это все равно что goto в коде. Насколько мне известно, только в Python исключения используются повсеместно, во всех остальных случаях — они достаточно редки.
              • 0
                я использую исключения для указания отказа пользователя от продолжения ввода данных.

                но, при ошибке, не так критично время — критично важно правильно ее обработать.
              • НЛО прилетело и опубликовало эту надпись здесь
                • 0
                  В итоге получается так, что «запись не найдена» и «недостаточно памяти» — это события одного уровня.
                  Т.е. опустив исключения на уровень работы в штатных ситуациях, вы потеряли однозначный механизм для сигнализации о серьезных проблемах.

                  Не уверен, что это хорошо.
                  • 0
                    Почему же. Исключения типизированы. Значит могут быть обработаны разным способом.
                    • 0
                      Я вижу тут возможные проблемы с инкапсуляцией (необходимо знать внутренние особенности функции/модуля), либо с дальнейшим изменением (что если вы обновили библиотеку, и она начала выкидывать новый тип исключения, который вы не предусмотрели? )
                      • 0
                        Если вы не предусмотрели какой-то тип исключения, то это значит, что в этом месте вы его не можете обработать — пусть летит дальше
                        • 0
                          А если это «штатное исключение», которое можно и нужно было обработать на этом уровне? Оно ведь у вас полетит на самый верхний уровень, тот, где обрабатываются серьезные ошибки, вроде «Cannot allocate memory».
                          • НЛО прилетело и опубликовало эту надпись здесь
                          • 0
                            Штатная ошибка — это ошибка, которая может быть обработана.
                            Если ее точка возникновения — вышележащий код, то такая обработка может быть сделана на месте без исключений. Иначе, выбрасывается исключение и проблема снимается с этого участка кода.
                          • НЛО прилетело и опубликовало эту надпись здесь
                            • 0
                              в крайней случае он вызывает std::terminate
                            • 0
                              А как вы определите, является исключение критичным, или нет? Придется вводить список критичных или некритичных исключений и поддерживать его в актуальном состоянии. Чревато ошибками, не так ли?
                              • 0
                                Есть же наследование. Вот его и используйте.
                              • НЛО прилетело и опубликовало эту надпись здесь
                  • НЛО прилетело и опубликовало эту надпись здесь
                    • 0
                      А кто мешает делать локальные типы исключений?
                      • НЛО прилетело и опубликовало эту надпись здесь
                    • 0
                      Предположим, что кодер Вася изменил функцию поиска записи, и она начала выкидывать новое исключение: «Найдено две записи». (Предположим, что он пишет инструмент для анализа целостности БД). Что произойдет с двумя вашими функциями?
                      • НЛО прилетело и опубликовало эту надпись здесь
                        • +1
                          похоже, хабр нуждается в статье про исключения.

                          желательно, без спецификации языка
                        • 0
                          Таким образом, логика первой функции уже возможно, когда-нибудь, будет нарушена. Вот это меня и смущает.
                          Для того, чтобы вернуть функции прежнее поведение, придется городить огород из try...catch, не так ли?
                          • 0
                            зачем?

                            поведение функции, которое нас интересует:
                            * функция выполнилась
                            * функция не выполнилась по причине такой-то ошибки (если мы хотим ее обработать)

                            Все другие ошибки нас не интересуют.

                            Или я просто не понимаю, что вас смущает
                            • 0
                              Меня смущает возможная быстрая раскрутка стека на самый верх в ситуациях, где этого можно было заранее гарантированно избежать. Частично может почь finally, но лишь частично и не во всех языках (
                              (Вот только в этой дискуссии понял, почему есть такие функции как get_last_error())

                              Смотрите, псевдокод, который использует возврат значения:

                              result = someFunction();
                              if (result == RESULT_ERROR){
                              resultCode = getLastError();
                              switch (resultCode){
                              // Какой-то код, который обрабатывает ошибки
                              RESULT_ERROR_FILE_NOT_FOUND: someAnotherFunction(); break;

                              default: throwError();
                              }
                              cleanup();
                              }


                              Ошибки функции будут гарантированно обработаны здесь, а исключительные ситуации будут гарантированно обработаны выше.
                              • 0
                                Опять не понял.

                                Зачистка стека, в идеале, должна происходит автоматически. В С++ это делается с помощью деструктора. То есть все временные состояния должны автоматически зачищаться при раскрутке.

                                Да и как можно гарантированно что-то избежать, если вы в этом месте не ожидаете чего-то, и не способны это обработать.
                                Как и в случае с исключением вы ожидаете некоторого кода возврата (некоторого типа исключения). Если код возврата вам неизвестен, вы сообщаете об этом наверх. Но, в этом случае, вы уже потеряете информацию об ошибке, теперь ошибкой стало выполнение вашей функции, а не вложенной.
                                • +1
                                  Иногда неважно, по какой точно причине не сработала функция, но необходимо это корректно обработать.

                                  Вы же не хотите, чтобы из-за неожиданного исключения, скажем в системе записи логов, программа падала по Unhandled exception?

                                  Т.е. вот такая функция может вызываться всегда и не прервет выполнения потока:

                                  // Returns 0 on success or -1 on failure
                                  function log(message){}

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

                                  // Throws WriteException on failure
                                  function log(message){}

                                  log — это только пример.
                                  • 0
                                    Она падает по не «Unhandled exception», а по причине: невозможно записать в такой-то файл.

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

                                    А теперь представьте, что кто-то напишет

                                    result = f(smth);

                                    if( result == err1 ) {
                                    do_cleanup();
                                    return err1023;
                                    }

                                    do_smth();



                                    Или вообще не проверит код возврата, потому что ему кажется, что это не важно (или это действительно не важно было в первой версии функции, которая обязательно делала что-то, а потом ее функционал расширился)
                                    • 0
                                      А если возможность записывать в такой-то файл является некритичной для приложения в целом?

                                      А если код возврата действительно не важен? В том то и разница — если код возврата не важен, его можно игнорировать. Исключения же игнорировать нельзя — их всегда надо обрабатывать, иначе — fail. В итоге, использование исключений увеличивает связность кода. С другой стороны, это заставляет обрабатывать многие ошибки во время разработки… Палка о двух концах, как всегда ;)

                                      P.S. Хороший холивар, но я пошел домой ;)
                                      • 0
                                        не повышает, а понижает. Ибо код делает только то, что от него просят
                                      • НЛО прилетело и опубликовало эту надпись здесь
                          • НЛО прилетело и опубликовало эту надпись здесь
                • 0
                  Это может работать в других языках, в которых слово «производительность» попадает под табу, зато все улыбаются и машут.
                  Но в С++ приведенный вами пример однозначно считается очень плохим дизайном.
                  • +1
                    В C++ тоже хорошим считается, если функция делает то, что ей говорят.

                    Если create — то либо create, либо исключение

                    Тогда можно без проблем писать код:
                    obj = create(smth);
                    obj.make_smth();
                    • 0
                      Это, как раз-таки, зависит от дизайна.
                      Если функция create может сфейлиться только из-за вселенских катаклизмов типа нехватки памяти, то она возвращает ссылку в случае успеха и кидает исключение в случае фейла. Вызывающий код в этом случае может обойтись без лишних проверок.
                      Если же там что-то сложное и нетривиальное, например, проверка входных параметров, то функция create возвращает указатель, который очень даже может быть 0, если что-то не так. И вызывающий код обязан проверять этот указатель.
                      • 0
                        Проверить? зачем?

                        Функция или отрабатывает, либо выбрасывает исключение. Не каких проверок вовне нету. Вот этот правильный и простой дизайн
                      • +1
                        Это прикладная мифология, что проверок нет. Чтобы сгенерировать исключение, всё-равно должен быть написан где-то if с анализом некого состояния системы. То есть, при использовании исключений для обработки ошибок, вы получаете if + накладные расходы на оформление кадра вызова функции, чтобы была возможность из неё откатиться.

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

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

                        В остальных случаях использовать исключения — это убивать производительность.
                        • 0
                          Абслютно с вами не согласен. Причины написаны выше. Но повторю:
                          * проверка идет только в самой низкоуровневой библиотеке
                          * любая функция уже заранее рассчитана на то, что через нее летит исключение
                          * исключение содержит куда больше информации об ошибке
                          * исключение как раз снижает связанность кода. При использовании кодов ошибок при добавлении возможной ошибки во вложенных функция может вырасти количество возможных ошибок в самой фукнции — и так по всему дереву вызова
                          • 0
                            проверка идет только в самой низкоуровневой библиотеке

                            Э? Если только там, значит это какая-то примитивная программа. Обычно, всякие ситуации, не дающие продолжить расчёт, и в других местах возникают. Я правильно понимаю, что низкий уровень — это всякие read/write/malloc?

                            А всякий throw будет обёрнут в if. Но и более того, вы сами говорите, что

                            любая функция уже заранее рассчитана на то, что через нее летит исключение

                            А для этого она должна быть особым образом оформлена. При каждом вызове в стек при вызове любой функции, которая потенциально выкидывает исключение, должна быть записана дополнительная информация. А это гораздо накладнее, чем дополнительный cmp/jmpсс (который предсказывается процессором, как не взятый) при возврате из функции.

                            Для оптимизаций тут в C++ даже предусмотрена специальная сигнатура для функции throw(), которая говорит, что эта функция ничего не пробрасывает через себя.

                            исключение содержит куда больше информации об ошибке

                            Кто заставляет при обработке ошибок возвращать int? Можно возвратить указатель на структуру любой сложности. NULL — ошибок нет, иначе уходим на разбор.

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

                            Так при оптимизации как раз и хорошо, то что связанность высокая. Если мы знаем, что в каком-то месте возник в условии if некий fasle, то можем просчитать на какой else он в итоге нас выведет, после прохода по цепочке других if-ов. оптимизацию jump-свёртки можем делать. С исключениями такого не провернуть.

                            Про рост ошибок — не верное утверждение. Ошибка она на то и ошибка, что если где-то возникла, то вся цепочка вызовов сворачивается с ошибочным состоянием. Поэтому, компилятор на Ура оптимизирует такие цепочки, когда inline делает.

                            Собственно, при ином поведении программ и исключения бы были бесполезны.
                            • 0
                              ох блин. я по моему уже все это описывал.

                              просто сейчас реально возразить времени нет. соглашусь только с тем, что использование исключений может наложить дополнительные требования и усложнить вызов функций. тот же throw() еще более ресурсоемкий, потому что ему, кроме того, что обработать полученные исключения, надо еще и перехватить любое из исключений внутри, и бросить std::termnate в случае ошибки.
                              • 0
                                А в чём предмет спора? Вы же сами подтверждаете тезис о том, что функция уже совсем не прозрачно вызывается. Там нужно кое-что дополнительно писать в память, а это дороже, чем cmp/jmp (заранее предсказанный).

                                Собственно, миф об отстутсвии накладных расходов при прямом (безошибочном) ходе программы разрушен.

                                В чём я не прав?.. Может, кинете ссылку на то, где Вы об этом писали?
                                • 0
                                  я не сижу причины, почему должны быть накладные расходы.
                                  • 0
                                    В смысле? А как вы свяжете throw с нужным try-catch? Эта связь — некая функция, называемая, функцией персонализации. Так вот, указатель на неё (в лучшем случае, некоторые компиляторы ещё сохраняют дополнительные параметры) и надо дополнительно сохранять в стеке (ну, или дополнительной таблице, это уже от компилятора зависит), каждый раз вызывая функцию, которая потенциально может нечто выбросить. И это, на самом деле, жуткий ужас по накладным расходам. Запись в память — дорогая операция.

                                    Но вообще, а что мешает просто посмотреть ассемблерный код, который генерируется для вызова чего-нибудь, выбрасывающего исключение?
                                    • 0
                                      Пока лень и работа. Но посмотрю.

                                      Я думаю, что процесс идет так:
                                      * бросаем исключение
                                      * идем по стеку, смотрим, какие функи были вызваны
                                      * смотри, есть ли в них блоки раскрутки исключений
                                      * вызываем если надо
                                      * идем дальше
                                • НЛО прилетело и опубликовало эту надпись здесь
                                  • 0
                                    Эх. Но возможность раскрутить стек не даётся за бесплатно. Чтобы его раскручивать, нужно же знать, где находить Program Counter'ы (PC) для возврата.

                                    Стеки при этом все разных размеров поэтому нужно, как минимум, хранить ссылку на предыдущий кадр (например, если речь о x86, то это push ebp; mov ebp,esp в прологе функции). И это никуда при оптимизации не выкинешь. Код, который не выкидывает исключений, часто запросто без этого обходится (у gcc даже опция на этот счёт есть -fomit-frame-pointer).

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

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

                                    Проверка кодов возврата да, нужна, если исключения не используются. И, может быть, так удобнее писать (хотя не уверен, ибо поток контроля управления получается очень таким спонтанным). Но по производительности, коды возврата быстрее, чем исключения.

                                    Как я уже много раз повторял, тут цена вопрос — это две коротких инструкции cmp (или даже test) и jmp, который будет предсказан, как не взятый. Для процессора это выполнить проще, чем организовать дополнительную запись в память.
                                    • 0
                                      Да. С этим соглашусь. А если надо сделать проверку десятка разных кодов?
                                      • 0
                                        Не знаю… Я всегда стараюсь минимизировать количество этих кодов, и использовать что-то вроде той обработки, которую применяли в исходниках half-life 2, или которая предлагается в Go.

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

                                        err = do1st()
                                        if(!err) {err = do2nd()}
                                        if(!err) {err = do3rd()}
                                        if(!err) {err = do4th()}
                                        if(!err) {} else {process(err)}

                                        вроде как быстро пролетает, когда ошибка не возникает. А когда возникает, то всё уходит на последний if, где можно уже структуру err поковырять в зависимости от ситуации.

                                        То же самое, можно писать и с goto (так, на самом деле, и пишут в основном)

                                        if(err) goto errStageX

                                        но за goto фанаты 'правильности' могут и побить.

                                        Да, несколько громоздко, конечно. Но эффективнее, чем исключения. И с логической точки зрения, вроде, даже удобнее. Потому что, ошибку можно учесть там, где она возникла: закрыть файлы, отрубить сокеты… А не размазывать это всё по всему дереву вызовов.
                                      • НЛО прилетело и опубликовало эту надпись здесь
                                    • НЛО прилетело и опубликовало эту надпись здесь
                                      • 0
                                        Так проблема, к сожалению, в том, что функция понятия не имеет, что за виртуальный метод она вызовет. И откуда её вызвали. Может, она вызывает тот метод, который может выкинуть исключение, которое ждут где-то наверху. Поэтому код должен быть почти всегда готов, особенно если оперирует с объектами с виртуальными методами.

                                        Поэтому игроделы, например, виртуальные методы особо не жалуют. Стараются избегать.

                                        В общем, компилятору в этом анализе приходится туго, и он не всегда справляется.
                                    • НЛО прилетело и опубликовало эту надпись здесь
                      • 0
                        Упс. Ответил не туда. Ответ был к посту выше.
                  • НЛО прилетело и опубликовало эту надпись здесь
                    • +1
                      Все в мире относительно, «медленные» исключения Си++ быстрее любого оператора Питона:)
                    • 0
                      Не всегда так. Вполне может быть оправдано написание функции createOrOpen
                      • НЛО прилетело и опубликовало эту надпись здесь
                        • 0
                          ну. или быть организовано низкоуровнево.
                          или сделать проверку перед созданием. и тд
            • +1
              По Страуструпу исключения должны выбрасываться только в случае критических ошибок, которые функция не имеет возможности как-то вменяемо обработать.
              Ситуации типа «файл не найден» или «юзер ввел не те данные» — это нормальные штатные режимы работы, которые надо нормально же обрабатывать в коде.
              Исключения же — это ситуации типа внезапной нехватки памяти, потери точности в вещественных вычислениях или bad_cast, когда оказалось невозможным приведение типов в рантайме.
              • +1
                Вот тут я с вами не соглашусь.

                Ситуация «фаил не найден» может быть как штатной, так и критической ошибкой
                • +1
                  Ну понятно что ситуацию можно выдумать любую, но в большинстве случаев «файл не найден» это именно ошибка, а не исключение.
                  • 0
                    У вас есть процесс обработки файлов. Нужно считать данные из 10 файлов, совместить их и залить в третий фаил.

                    Вдруг не найден один из файлов. Как код, который ДОЛЖЕН считать данные из него, может адекватно обработать ошибку?
                    • 0
                      Ошибки надо обрабатывать там, где их можно обработать (привет, кэп!)
                      Откуда у вас берутся эти 10 файлов? Из командной строки? Из конфига? Из гуя? Вот в этом месте их и нужно проверять и открывать. И если что-то не так — материть юзера.
                      • +1
                        А если фаил поврежден. Если его структура невалидна?
                        Это тоже проверять в точке ввода данных?
                • +1
                  Вопрос в доверии, если доверяем тому, что файл точно есть, а его нету, то это исключительная ситуация, а пользователю доверять априори нельзя! Посему любой неправильный ввод — это штатная ситуация.
                  • 0
                    А является ли штатной ситуацией нарушение формата файла? Пользователю то доверять нельзя.
              • НЛО прилетело и опубликовало эту надпись здесь
                • 0
                  Вообще, исключения типа фаил не найден, обрабатывает в самом низу (если говорить о gui), тем самым прерывая всю задачу, данную пользователем.
                  И это правильно
                  • НЛО прилетело и опубликовало эту надпись здесь
                    • 0
                      Ну вот тут специфичная обработка. Он ОЧЕНЬ редко в GUI нужна
            • 0
              >А ведь исключения выбрасываются далеко не только в случае критических ошибок — это вполне штатная ситуация, так сказать, способ коммуникации между функциями.

              Как только я прочитал эту фразу, я сразу понял, что далее будет дли-и-и-инный тред на эту тему. Я не ошибся :)
          • 0
            единообразием
            • 0
              единообразием с чем?
              • 0
                когда есть куча библиотек, лучше использовать единый общий с библиотеками подход, а не думать каждый раз, вызываешь ли ты библиотеку или свой код, будет он возвращать исключения или не будет. Меньше возможности ошибиться, если использовать одинаковый с библиотеками подход.
                • 0
                  я всегда считаю, что может произойти исключение. Но в 99.9% тебе не надо даже задумываться об этом
                  • 0
                    задумываться надо — thread cancellation
                    • 0
                      не понял, при чем тут это?
                      • 0
                        к тому, что когда «всегда считаю» следует учитывать и такие тонкости. Т.е. это не возражение какого-либо вида, просто дополнительная инфа по теме :-)
                        • 0
                          Ну если ты творишь такие вещи, как перехват всех исключений — то ты ССЗБ.

                          Хотя я творю, но там у меня «легковесные потоки»
    • +4
      А если посмотреть на стайлгаид гуглa — динозаврики, ни дать ни взять:

      google-styleguide.googlecode.com/svn/trunk/cppguide.xml

      То же и с Qt, mozilla, многими другими проектами.
      Пример довольно успешного пректа, мощно использующего фичи С++ — llvm.org
      Рекомендую к прочтению кода. Лучше многих книг.
    • 0
      Я в начале статьи отметил, что она не относится к системному программированию, а это именно то, чем занимается Линус и компания. Но Вы, конечно, правы — хороший или плохой код можно написать в любом языке.
    • 0
      Ядерщики не на плюсах пишут. Там чистый Ц.
  • 0
    На картинке бага?
    #include void main() < — может так все-ж? Или C++ настолько суровей C?
    • 0
      че-т сожралось…
      #include iostream
      void main()
      • +12
        вообще по стандарту С++ функция main возвращает int.
        • –2
          error C4430: missing type specifier — int assumed. Note: C++ does not support default-int
    • 0
      Да там еще и пространство имен std куда-то затерялось )
      • 0
        Во времена <iostream.h> его не было… в том же Borland например.
        • +6
          Я настолько был уверен, что «Hello world» не получится написать так, чтобы не возникло вопросов, что скопировал его из соответствующей статьи Википедии. А всё-равно не помогло :)
          • +3
            C++ настолько суров, что даже в хелловорлде можно наделать пяток багов %)
    • 0
      Там ещё пространство имен std не указано. :3
    • +2
      По старинке возвращаемый тип у функции int. Наследие С. ISO C++ запрещает такое объявление:

      error: ISO C++ forbids declaration of 'main' with no type

      Конечно, стоит использовать -Wall или аналог.
  • +1
    Обычно в подобных постах вспоминают страшное «проклятие» C++: обратная совместимость с C.
    И действительно, люди учившиеся давно или даже недавно, но у преподавателей учившихся давно, программируя на C++ все равно пишут код на C, пытаясь его «обернуть» в какие-то C++ конструкции, не понимаю того чего делают, чего теряют и какой кошмар творят. Такое происходит сплошь и рядом.
  • +4
    Также я буду говорить только о прикладном программировании под десктоп-операционки (в кластерах, микропроцессорах и системном программировании тенденции могут отличаться).


    Очень хорошо, что Вы сделали эту оговорку. Использовать STL и Boost в системном программировании, конечно, заманчиво, но далеко не всегда представляется возможным.

  • НЛО прилетело и опубликовало эту надпись здесь
  • +22
    >Счет памяти и ресурсов процессора шел на байты и такты, многие вещи еще не были изобретены и приходилось выкручиваться. Но это вовсе не повод и сегодня писать код исходя из этих предпосылок.

    Вот благодаря таким умникам мы имеем многочисленных монстров на десктопов, которые чтобы отрисовать пару менюшек требуют не меньше 2Гб рамы. Особенно в линупсе замечал: кде4, гноме, фаерфокс (вообще взрыв мозга), опенофис (!!!!!) жрут всю память что находят и еще в свап зарываются. И при этом все тооормооозит и глючит.
    Современных разрабов нужно запирать в комнате с компом в котором 128мб оперативки и пусть кодят и работают на нем.
    • НЛО прилетело и опубликовало эту надпись здесь
      • +5
        Программистов надо научить одной, но самой сложной вещи: думать
    • +4
      … и спустя 5 лет они напишут все то же самое что и сейчас, работающее на 128 памяти, когда прогресс уйдет далеко вперед, а цена на 4гб памяти упадет с нынешних 1300 рублей до каких-нибудь ста рублей…
      • 0
        а потом появится новое поколение, программисты которого будут писать код, жрущий уже 6 гб просто так, «чиста по приколу»?)
    • –3
      Просто поставьте себе больше «рамы», она сейчас не дорогая. Зачем «разрабов» мучить?
      • +7
        Следствием апгрейда компьютера хотелось бы видеть возможность запустить еще больше тяжелых программ одновременно, видеть улучшение времени отклика интерфейса, времени выполнения задач.
        А получается, что платишь, платишь деньги за апгрейд, а работает все примерно с такой же скоростью и все так же запустить более 2-3 тяжелых приложений даже на 4-6Гб рамы является извращением. Зато программистам легко, да)

        З.Ы. Прошу не считать этот коммент холиварным, ибо я абсолютно согласен с тем, что для каждой задачи — свой инструмент; я не стесняюсь использовать дотнет, когда надо быстро набросать гуевое приложение под винду.
        • +1
          Запускайте старые версии, вам же достаточно тех возможностей.
          • 0
            угу, и потерять возможность открывать файлы, созданные другими людьми в более новых версиях; в случае с виндой например — потерять возможность запускать вообще любой новый софт; в случае с линухом (и не только) — отказаться от поддержки нового железа…
            Список можно продолжать… Мы все вынуждены переходить на новые версии софта.
            • +1
              Ну насчет открытия файлов созданных в новых версиях, можно просить их экспортировать в какой-то другой формат.
              Насчет винды, тот же .net не слабо упрощает разработку программ, но тоже требует ресурсов.
              Насчет линуха, ну дык поддержка нового железа тоже ресурсы и место отнимает, то же автомонтирование например.
              • 0
                Да, но рост требований программного обеспечения к железу просто геометрический)
                И это неприятно.
                • +3
                  Ну дык и рост требований пользователей к ПО тоже такой же.
                  Недавно только все смотрели CD рипы, а уже всем нужен блюрей, который весит в десятки раз больше.
                  Недавно все сидели на квадратных окнах, а теперь всем нужны закругленные окна с прозрачностью и тенями.
                  Недавно все сидели на статичных страницах, а теперь всем нужен js, гугл мапсы, видео и семантичная верстка. И чтобы сайтов было много, и чтобы они были качественные.
                  Почему-то никто не хочет монтировать руками флешки, ждать выхода дров месяцами, сидеть на дефолтном опенбоксе и links, а текст редактировать в abiword.
                  Причем люди не готовы платить за это деньги и ждать годами.
                  П.С.
                  Да и насчет 2-3 тяжелых приложений вы тоже лукавите. 10 лет назад чтобы держать открытыми 3d max, photoshop и corel draw одновременно с открытми файлами в них нужна была очень мощная машина.
                  • +2
                    ну при увеличение картинки видео в 2 раза сложность обработки растет более чем в 2 раза
                    • 0
                      Я про то и говорил.
                  • –1
                    Да, вот только сд рипы мы смотрели на пентиум4-3 ггц, а бд рипы уже на кореN-3ггц, у которых рост производительности на ядро составил примерно процентов 40%.
                    Пора уже понять, что рост вычислительной мощи на ядро закончился, а на 8 ядер в одном кристалле с очень медленной общей памятью далеко не все задачи можно раскидать.
                    • 0
                      Как раз разница примерно в 6 раз идет между VCD и BD, причем это чисто по разрешению, не учитывая то, что на VCD хуже качество видео и звука.
                    • 0
                      сд рипы можно было на первом пне смотреть :)
                      был такой плеер досовский, qvpro вроде.
                    • 0
                      CD рипы и DVD я смотрел на Duron 850 Mhz
    • +12
      Меня всегда удивляют такие комменты. Вы понимаете, что одна страничка хабра весит 500 килобайт? А то, что это все еще надо визуализировать с прозрачностью, в нужном порядке и с динамикой(или вам хочется отвечать на коммент на отдельной странице?), то, что это надо хранить не в виде текста, а в виде дерева, чтобы можно было манипулировать через яваскрипт, тот же жрущий флеш и html5, те же сандбоксы, чтобы браузер не валился целиком а только плагин.
      И не забудьте, что это должно работать сегодня, а не завтра. И браузер у вас никто не будет покупать, а будут использовать бесплатно.

      На словах умных много, а не деле ближайший аналог тот же хром жрет еще больше.
      • +3
        Обычно не правы обе стороны в категоричных суждениях.
        Есть куча говнокода, который жрет ресурсы — это плохие программисты.
        Но есть и куча задач, который тоже жрут ресурсы.
        • +8
          Одно дело говнокод, другое дело просто не оптимизированный код, потому что нет времени, ресурсов, возможности, желания. Даже если разработчику лень оптимизировать код какого-то приложения, например kde4, написав неоптимизированный код он уже сделал во много раз больше чем тысяча хомячков возмущающихся насчет этого кода.
      • +1
        ну да, главная хабра 500кб. из них ровно 18кб полезного текста (в уникоде), все остальное — overbloat. 500кб это сравнимо по размерам с войной и миром. И да, тормозит хабра немеряно, в частности из-за флэш банеров макбук разгоняет моторы в подготовке к выходу на третью космическую, а под линуксом отельные статьи скролятся с 2фпс из-за скрипта новых комментов (да, я еще помню как хорошо все работало до его введения). Спасибо, хабра — это классический пример того как не надо делать сайт и как можно испортить хорошее, стремлением к «лучшему».
        • 0
          Ну ведь есть еще разметка, оформление. Если хотите смотреть без них, можно смотреть через тот же elinks. Там и не тормозит.
          Кроме хабра есть тот же гмайл, где тоже много чего незаметно на первый взгляд.
        • 0
          «Флэш-баннеры....»

          Очнитесь, на дворе XXI век! Уже давно изобретены ад-блокеры ;)
        • 0
          Ковырял я причину тормозов. Называется она mootools. Отключаешь библиотеку, и Хабр летает
    • 0
      >Особенно в линупсе замечал: кде4, гноме, фаерфокс (вообще взрыв мозга), опенофис (!!!!!) жрут всю память что находят и еще в свап зарываются.

      Вы, возможно, не поверите, но в линуксе подобное поведение сделано намеренно, и данные с диска по максимуму кэшируются в памяти, сбрасываясь на диск по необходимости. Дадите гиг — заберут гиг, дадите четыре — со временем заберут и четыре. И это правильно, потому что память должна работать (не путать с «память нынче дешёвая»). Если у меня 4 гига — то нафига заставлять систему лишний раз трепать диск, если можно хранить те же данные в оперативе?
      • 0
        Так эта память под файлы не записывается на счёт приложения. Но предыдущий оратор как-то не прав. Например, у меня firefox сейчас жрёт около 120 мегабайт памяти, показывая вот это обсуждение (а оно не хилое такое).

        Хотя, openoffice мозг взрывает, конечно, но нужно учитывать, что туда Java ещё намешана.
      • 0
        друг, я сам раз 10 точно объяснял в подробностях разным людям (втч и сотрудникам) что такое память в линуксе и как она работает и почему ее почти никогда нет свободной. Это все легко и расписано школьниками в интернетах. Вот что я не мог объяснить, так это почему система у которой свободно порядка 70% (50-70Гб) оперативки активно уходит в свап вместо того чтоб освобождать кэши. И даже в рэдхате нам ничего не смогли по этому поводу посоветовать.

        Речь конечно шла о том что ФФ+ООо+кде4 легко оккупируют 1-1.5Гб оперативки. При том что пару лет назад оно все летало на 256мб.
        • 0
          Планировщики памяти бывают разными. Есть несколько поведений:
          1. Выделяем кусок памяти, который максимально подходит под размеры приложения. Фрагментация памяти будет низкой, если возникнет приложение, которое потребует много памяти и сразу, система легко ее выделит
          2. Выделяем как можно больший кусок памяти. Приложение может быстро выполниться и освободить все тот же большой кусок свободной памяти.
          3. Выделяем константу памяти, например, выделяем приложению всегда 1/10 свободной памяти.
          4. Не освобождаем память, если ее дофига свободной.
          И еще много-много разных поведений.

          Так вот, в некоторых случаях планировщику проще скинуть на винт контекст, чтобы освободить максимум ресурсов для более активного приложения, чтобы повысить отзывчивость системы именно в активом приложении.
  • +1
    Напомню, это поддерживается в основных, стабильных (не альфа\бета) ветках всех основных компиляторов.
    Расскажите это Apple, которые до сих пор поставляют gcc 4.2.1.
    • 0
      Я осознаю, что не всё еще в мире гладко и в некоторых ситуациях приходится прогибаться под изменчивый мир. Но они никуда не денутся.
      • +2
        Apple семимильными шагами пилит clang. Год назад я думал, что ждать еще года 2-3. А он уже есть
        • 0
          И тама пока C++0x очень уж странно работает, точнее не работает. Попытался их libc++ заюзать, так он её сам и не увидел. Как ему её скормить?
          • 0
            у меня в xCode смог. Но глубже не копал
            • 0
              Я вообще собирал libc++ в Генте, хотя официально написано, что они её в Линуксе не тестили.
              • 0
                хз. на лялихе мне лень
                а вот в портах вчера clang собрался без проблем
                • 0
                  Да собирается то он прекрасно, но libc++ не подхватывает. Точнее не видит его iclude'ов
                  в результате чего
                  #include
                  и т.п. не обрабатываются
                  • 0
                    надо курить. на работе у меня gcc пока что
      • +3
        Есть кстати и другие причины, «почему мы до сих пор это не используем».
        Во-первых, менять компилятор, при нашем объеме кода это очень долго и дорого. Мы уже меняли с VS2003 на VS2008 и с gcc 4.0 на 4.2 и знаем не понаслышке.
        Во-вторых, не все 3rd-party библиотеки, которые у нас используются, есть для VS2010.
      • +2
        Нужен им этот C++0x. Они Obj-C расширяют. Вот и блоки кода появились.
        • 0
          а obj-c++

          не стоит думать об ограниченности apple
          • 0
            Вы пробовали выполнить проект на objective-c хотя бы с использованием stl?
            • 0
              Ад и Израиль, вот, что это такое! Выглядит такой код дико.
              Приходилось obj-c мешать с C++/Qt
              • 0
                вот и я мешал. Но в ограниченных объемах
                • 0
                  Да я тоже, на хакинтоше вдволь не наиграешься.
                  • +1
                    Не правда. У меня хакинтошь 24*7
                    • 0
                      И amd проц и видео ati 57xx? И звук 5.1 работает? не-не-не
                      В огрызке не интересно играться.
                      • 0
                        ну вот тут нужен большой шаманский бубен. и клок волос джобса.

                        у меня все железо под хак собиралось
                      • 0
                        На видео 57хх сейчас нативные драйвера эппл выпустила, а звук 5.1 — да, играет. И вайфай — да, работает. И даже блютуз работает. Правда проц интел.
              • 0
                Вот я о том же — получается месиво из врапперов, адаптеров и костылей.
                • 0
                  Не. я имел другое
    • –2
      Из портов ставиться 4.6
      • 0
        Там бета.
        • 0
          Ну да, 4.6.1 бета. Жалобы конечно есть (они есть на версии для всех ОС), но люди на нем собирают. Подробнее тут и тут.
          • 0
            Вроде уже не бета… Хотя вчера что-то не собралось. Из-за x86_64

            Танцую с бубном
          • 0
            Читаем внимательно начало ветки. Фраза автора, к которой я написал комментарий:
            Напомню, это поддерживается в основных, стабильных (не альфа\бета) ветках всех основных компиляторов.


            Ну а вообще продакшен код компилировать бетой — моветон.
            • 0
              Насчет беты — это бета 4.6.1, а не 4.6.0. 4.6.0 для дарвина такая же как и у всех остальных.

              Я сам на Cях не программирую, просто иногда собираю софт с исходников. Но знакомые говорят, что с 4.5 были проблемы с Obj-C, а с 4.6 вроде все нормально.
              • 0
                Я не знаю, как из портов поставить стейбл:

                % port info gcc46
                gcc46 @4.6-20110325 (lang)
                Variants: gfortran, java, universal

                Description: The GNU compiler collection, including front ends for C, C++, Objective-C and Objective-C++. This is a prerelease BETA
                version and does not have all available language front ends enabled.


                Но даже если бы можно было — врядли бы мы решились на переезд без веских причин.
                • 0
                  В портах идет самая последняя, так как беты все лучше и лучше работают на osx, поэтому туда включают самый последний код. И так еще будет как минимум пару месяцев.

                  А насчет переезда, то тут конечно спешить некуда, но чисто для освоение новых фич и тестирования стабильности, мне кажется, это было бы уместно.
  • +1
    Cтандарты — хорошо. Тут дело скорее в следующем обратная совместимость играет опредленную роль не
    всегда есть возможность использовать последний gcc 4.x, в некоторых ообластях например en.wikipedia.org/wiki/Embedded_system… Toolchain «замерз» на «древних компиляторх».
    Образно говоря иногда предпочтение отдается даже «C» ввиду всё той же портируемости между платформами. В тоже время можно найти полноценные C++ библиотеки при желании.

    Ввиду всё той же обратной совместимости можно компилировать последние версии php ,ruby На Solaris и прочие вещи :) К этому можно добавить что в Юникс Системах например в POSIX стандарте прописано поведение функциий стандартной библиотеки, там также pubs.opengroup.org/onlinepubs/9699919799/.
    Есть тесты которыми можно проверить совместимость, есть также дыры в реализации, не понятно что куда вернется при определнных вызовах.

    Программы на C/C++ как снежный ком, все используют общие блоки, т.е. если переписать какую то популярную библиотеку на с++ чистый к примеру взять тот же zlib возникнут небольшие проблемы у тех кто ей пользуется не для всех платформ соберется.
  • 0
    Лет 20 назад бытовало выражение «синдром калькулятора», означавшее стремление к экономии каждого байта.
  • +2
    1. Цитата: «Все, что я буду дальше писать касается только программирования на С++ и только mainstream-компиляторов (gcc, Intel, Microsoft).»

    Осталось для каждого компилятора выписать минимальную версию, поддерживающую ништяки, описанные тут, и все сразу же встанет на свои места.

    2. Цитата: «Также я буду говорить только о прикладном программировании под десктоп-операционки.»

    В купе с п. 1 мы оставляем за бортом огромное поле, на котором ведется разработка на С++.
    • 0
      На счет версий — Википедия в помощь. На счет «оставляем за бортом огромное поле» — согласен. Но я вообще не могу представить себе какое-нибудь утверждение, справедливое на всей области использования языка С++.
      • +3
        К примеру:
        Возьмем самую распространенную среду разработки под самую распространенную ОС. С++0х поддерживается пока только в 2010 версии.
        Много у вас знакомых, разрабатывающих в последней версии студии?
        • +2
          … особенно в лицензионной
        • +1
          У меня достаточно много.
          Но это скорее всего специфика C# направления в разработке.
          • 0
            имелись в виду естественно С++-программисты.
        • –1
          >Много у вас знакомых, разрабатывающих в последней версии студии?
          Пару десятков. А зачем писать в НЕ-последней?
          • +2
            Что значит «зачем» если ее тупо нету, например?
            • 0
              Если существует хотя бы одна версия, то существует версия, которая на данный момент является последней.
              • +2
                Бывает платный софт, где за апгрейд версии нужно платить.
                Привет, кэп.
                • +2
                  «Не закуплено фирмой» и «тупо нету» — разные вещи.
              • +1
                «тупо нету» = «нет в наличии у программиста».
                Возьметесь объяснить необходимость покупки последней студии абсолютно всем работодателям?
                • +1
                  Я — нет, для этого у МС есть целый штат маркетологов, это их работа. Но могу посоветовать показать работодателям эту статью, которая не «маркетинговый бред», а «разработчикам от разработчиков».
                  • +1
                    смешно, но нужна статья «работодателям от разработчиков».
                  • 0
                    Понимаете, переезд «почти готового» проекта на новую версию компилятора, как правило, не лучшая идея. Свои-то баги еще не пофиксили, зачем еще новые вносить?
                    Поэтому обновление среды обычно происходит между проектами или между мажорными версиями проекта.
                    • 0
                      А с этим аспектом я и не спорю — это разумно ;)
  • 0
    a << 1; // наверное, опечатка, имелось ввиду a = a << 1;
    • 0
      a <<= 1; // больше нра
    • 0
      Да, конечно, опечатка. Спасибо, исправил.
  • 0
    Большая проблема заключается в том, что в стабильной инфраструктуре используются часто стандартные пакеты для компиляторов, а вот большинство разработчиков не спешат обновлять в своих продуктах данные пакеты.
  • +5
    Сложилось мнение, что кто-то поспешил показать свою непревзойденность.
    Может топик и не плох, но стиль изложения угнетает.
    Сами написали «Люди имеют завышенную самооценку, считают себя умнее всех», так пускай все сам и решают что для них надо, а что нет. Рано или поздно один заметит проблемы в своем стиле и коде и начнет развиваться самостоятельно, а другой наоборот погрязнет во всех своих недочетах с головой и сменит профессию.
    Программист он на то и программист, что умеет самостоятельно находить решение для конкретных задач и грамотно воплощать его в реальность.
    • +1
      Если это был их личный код…
      • –1
        Так а в чем проблема? Значит не надо нанимать на работу не грамотных программистов, ну или по крайней мере доверять им что-то столь важное или серьезное.
        Если же наоборот по воле работодателя работаете вместе с такими, то тут либо вы нашли не правильную работу, либо вы на самом деле ни чем не отличаетесь от тех с кем вы работаете.

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

          Но он очень хорошо работает, чтоб его переписывать.
  • 0
    Простите, а накой хрен в 21ом веке что-то массово писать на C++?

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

    >Если речь идет о количестве людей, предметов, транзакций, температуре, дате, расстоянии, размерах файлов и т.д. — пользуйтесь типами long, longlong или чем-то специализированным. Забудьте о byte, short и int.

    За выбор типа данных от балды нужно убивать в первую очередь.
    • +4
      А почему бы не писать? Это не сложно
      • НЛО прилетело и опубликовало эту надпись здесь
    • +5
      >драйвера на сишечке, бутлоадеры на асме и адскую SIMD-магию в кодеках.
      А еще игры, CAD-системы, видеоредакторы, компиляторы всех остальных языков, операционки, низкоуровневые библиотеки, браузеры… продолжать?
      • –2
        >А еще игры, CAD-системы, видеоредакторы, компиляторы всех остальных языков, операционки, низкоуровневые библиотеки, браузеры… продолжать?

        да-да, конечно, в кого не плюнь — все пишут компиляторы, причем на плюсах, а компилируют они исключительно сами себя, а не какой-то прикладной код.
        • +4
          а чего Вы за компиляторы зацепились? Остальных примеров мало? Ну добавьте сюда еще виртуальные машины, микропроцессоры, стеки протоколов, разнообразные DirectX и OpenGL-программы, файловые системы, БИОСы… продолжать?
        • +2
          Знаете, когда десктопный софт тормозит это уныние навевает. Он должен быть максимально быстрым! Это не Ъынтерпрайз, где хочешь не хочешь, а работать на тормозном софте заставим!
          Ах да, не забываем про мобилы и планшеты.
    • +1
      Тот же C# и Java позволяют не думать о некоторых вещах, например об удалении памяти. Но если вы пишете серьёзный проект, то вам всё равно рано или поздно придётся об этом думать и возможность контролировать этот ресурс напрямую скорее плюс, чем минус.

      Если надо что-то запрограммить на скорую руку просто чтобы работало, то С++ может оказаться не лучшим выбором, это да.
      • +3
        >Тот же C# и Java позволяют не думать о некоторых вещах, например об удалении памяти.

        Заблуждение, которое приводит к тому, что софт на джаве памяти жрёт больше, чем С++ софт даже с наличием утечек.
        • +1
          Собственно я об этом и говорю. В шарпе и джаве о контроле над памятью можно забыть и жить спокойно до поры до времени, но это аукнется. Память — это ресурс и управляешь ли ты им в явном виде или неявном, думать о нём всё равно надо.
  • +5
    Ага, в танке! Более менее с++0x в gcc появилось только с выходом 4.6 менее месяца назад (25го марта, а на дворе 12 апреля)
    А в msvc тоже не всё гладко, согласно табличке:
    blogs.msdn.com/b/vcblog/archive/2010/04/06/c-0x-core-language-features-in-vc10-the-table.aspx
    не видно нового for через итерабельные типы, не видно Initializer lists.

    В обоих невидно наследуемых/делегируемых конструкторов и Non-static data member initializers (мелочевки которая очень упрощает жизнь)
    • 0
      Вообще, в мире IT, отставать на месяц — это часто почти аналог «быть в танке» :)
      На студию Вы зря наехали, 2 неподдерживаемые и 5 частично поддерживаемых фич из 23 — весьма недурно.
      • +1
        Вообще-то менять компилятор, а за ним и дизайн проекта каждый месяц можно только в мыслях.
      • 0
        Только фич этих более 50 если верить табличке gcc :)
  • 0
    Я сам буквально недавно заинтересовался С++0x.

    Действительно потрясающе. Но есть одно «но»: поддержка компиляторами.

    Если в *никсах все более или менее неплохо — вот в новой убунте уже 4.5 gcc идет по дефолту, а 4.6 можно прикрутить в пару пинков), который поддерживает довольно солидный сабсет С++0х, то с MSVS 2010 все довольно пасмурно. Вот табличка того что он поддерживает. Конечно неплохо, но… А в некоторых других реализациях компиляторов еще менее радужно (например в LLVM C++ и с инициализаторами туго, и с лямбдами).

    А так хотелось бы иметь и там возможность писать что-то вроде (компилябельно на gcc 4.6):
    vector<pair<string,int>> v = { {"one",1} ,{"two",2},{"three",3} };
    for(auto i : v ) { cout<<i.first<<"="<<i.second<<endl; };
    

    (а вот заменить вроде уже на array нельзя, так как std::array уже не может взять свой размер с initializer list. хотя надо попробовать добавить...)
  • –5
    Последней строкой должно быть — и вообще есть более современный язык — C#.

    Перешёл с C++ на C#, нравится. Хотя у C++ есть какое своё обаяние…
    • +3
      и про дот.нет не забудьте. И все это на линукс-сервере обрабатывающем запросы
    • +2
      C# — неплохой язык, но всё же ограниченный. Скажем так, не всегда, не везде и не так просто его можно использовать. Возможно, было бы веселее, если бы в Mono была реализация WPF, а не только устаревшего WinForms. Но чего нет — того нет.

      C++ меня привлекает ещё и своей универсальностью. Вероятность того, что в реальном проекте встретится задача, не реализуемая на C++ ни под каким соусом, очень невелика. А вопросы быстрой разработки кода (пусть и в ущерб производительности) во многом решаются существующими фреймворками.
    • +1
      Только для Windows

      fixed.

      в mono далеко не всё так прекрасно.
  • +1
    Как-то слишком много ляпов в статье.
    0х это здорово, но с поддержкой пока все очень сложно. Если бы автор реально что-то кодил, он бы это знал.
    Код для примеров тоже вызывает улыбку: понятно, что опечататься может каждый, но так возьми любимый компилятор и проверть код на ошибки.
    Ну и эпичный финал — взять helloworld из Википедии;) и после этого писать статью в стиля «я тут папко, а вы йуные подаваны»
    • +1
      Буду благодарен за указания на ляпы. И да, я реально пишу с использованием С++0х.
      А helloworld — так это вообще фотка наскальной живописи на стене пещеры. Автор, конечно же, не я. Миллион лет рисунку. :)
      • 0
        Ну, так, на вскидку:
        а<<1 вам уже указали;
        в части про векторы и auto как минимум два ляпа — первый это явная опечатка, второй это несоответствие «старого» и «нового» кода;
        MySuperVector очевидно должен быть шаблоном;
        Про хелловорлд, пусть даже на картинке, вам уже сказали.
        Итого из 6 маленьких демонстрационных кусочков кода у вас проблемы в 4. Многовато для гуру.
        И да, на мой взгляд rvalue-ссылки никак не заслуживают упоминания под номером 2 в списке фич 0х, 99.9% не знакомых с ними программистов, увидев ЭТО скажут omfg wtf?
        • +1
          1. Побитовый сдвиг был приведен как пример кода, который плохо читается и понимается и как писать не надо. Я так и не пишу — потому и опечатался. Опечататься в а*=2 намного сложнее. Так что лишняя иллюстрация к тезису вышла.
          2. За опечатку в примере векторов спасибо.
          3. «MySuperVector очевидно должен быть шаблоном». Конечно должно быть! И еще и по-хорошему, унаследованным от vector или вообще на него замененным. Об том же и речь была, что это НЕВЕРНЫЙ код.
          4. Хелловорлд, во-первых — картинка на спине мамонта (что уже навевает серьёзное к ней отношение), во-вторых — из Вики. Да, я пользуюсь Вики при написании статьи. Что в этом плохого?
          5. Список не отсортирован по важности или полезности фич, так как эти вещи — повод для холивара.

          Спасибо за критику, Вы помогаете статье стать лучше.
          • 0
            Наследоваться от стандартного вектора? Уууу… Спрячьте это немедленно, пока вас не заклевали.
            • +2
              Хочу и наследуюсь. Чем плохо?
              • +2
                Тем, что стандартные контейнеры просто не предназначены для того, чтобы от них наследоваться. В Java они бы имели атрибут final.
                У стандартного вектора нет виртуальных функций, причем это сделано сознательно, ради скорости. Это означает, что все функции, принимающие в качестве параметров стандартный вектор, имеют почти все методы стандартного вектора зашитыми глубоко внутрь кода. Если вы передадите в такую функцию свой вектор с перегруженными методами — функция все равно будет использовать методы стандартного вектора и может угробить ваши данные (полиморфизм не включится). Если вы передадите в такую функцию перегруженный вектор с дополнительными полями данных — функция может разрушить эти поля данных или даже произвольную область памяти (потому что она о них ничего не знает, а полиморфизм не включится). Причем эта «опасная» функция может вызываться где-то внутри библиотеки, о чем вы вообще не подозреваете. Поэтому наследование от вектора — это бомба замедленного действия, которая неизвестно когда рванет.
                Все, что вы можете сделать безопасно — это добавить к стандартной реализации вектора какие-то свои функции, не имеющие отношения к работе собственно вектора. Но для этого не нужно наследоваться, Си++ позволяет определять функции, в том числе виртуальные, и без этого.
                Но действительно правильным решением будет создание обертки вокруг стандартного вектора, то есть класса с private: vector v;.
                • +1
                  эти ограничения применимы почти к любым типам без полиморфного поведения
                  • +1
                    А зачем тогда спрашивать?:)
                    • +1
                      Затем, что такое ограничение стандартно и о нем надо помнить.

                      А наследоваться можно, если добавлять поведение, а не менять.
              • +2
                > Хочу и наследуюсь. Чем плохо?

                «
                • Люди нифига не читают умные книги
                • Люди имеют завышенную самооценку, считают себя умнее всех
                »
  • +2
    Мужик, искренне жму руку.
    Я с самого начала своего пути, как только вывел свое первое hello world на паскале, извечно спорил с людьми, которые напрочь откидали новое и тыкали носом в, чуть ли не бинарный, код и крича, что это основа, это то, что надо и должно быть только так! Согласен, основы знать надо, но застегивать пуговицы через задницу китайскими палочками…

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

    Статья на актуальна, на злобу дня =))) Но увы, есть уйма упёртых =(
    • 0
      Пусть пишут. Технологии когда нить победят толпу
  • +3
    Всегда компромисс. Технологий, этапов, ресурсов. Ощущения, подобные вашим, я испытал когда перебрался на Python после C++. Прототип пишется намного быстрей. Просто забываешь о ненужных на данном этапе вещах. Но на production этапе, особенно если алгоритм ресурсоёмкий, выигрыш может быть в сотни и тысячи раз, если написать то же самое на C++. Но если сравнимо или разница небольшая, то да, легче пожертвовать лишними Mb оперативки ради удобства поддержки и времени. Конечно в случае, если пользователь не испытывает от этого никаких неудобств. Задачи всегда разные, как и способы их решения.
  • 0
    Спасибо. Не то, чтобы я этого не знал, но а) сведено воедино и б) сведено блистательно.
  • –3
    Видя во что превращается С++, давно пересел на Си. Жиреете уже от «синтаксического сахара».
    И параллели у вас не в тему. Если ваша подруга попросила одолжить у вас машину, пойдете купите новую?
    • +1
      Ну, насчёт отказываться от C++ — не соглашусь. Просто нужно понимать, что для каждой задачи свой язык. И если лучше Си, то лучше его и использовать. Но это не означает, что Си лучше везде и всегда.

      А вот насчёт передачи по указателю — тут автор дал маху. Представляю себе потребление стека, если передаваться всё будет по значению, и сколько на этом будет потеряно времени. Ужасно плохой совет. А уж если это не просто структура, а класс…
      • 0
        есть ссылки же
        • 0
          Ссылки — те же неявные указатели. Автор их уравнял в своём тексте.
          • 0
            хм. где?
            • 0
              В заголовке раздела уже:
              Передача всего и везде по указателям (ссылкам)
      • 0
        На самом деле если объекты маленькие, или у них хорошо реализован COW, можно и по значению передавать без проблем. Автор же не советовал отказаться от передачи по указателю/ссылке полностью, он написал
        передавать сущности в функции и методы как по ссылке так и по значению — очень мощный механизм и не стоит его использовать однобоко
        • +1
          Почти никогда не требуется передавать по значению, кроме как это совсем маленькая структура. Аргумент — его могут изменить — как-то смотрится мега дико. Я бы даже сказал немного паранойей попахивает. «const_cast» в проекте вообще должен отсутствовать.
          • +1
            Ну вот алгоритмы STL принимают итераторы по значению, а не по константной ссылке.
            • 0
              Я же сказал — «кроме как это совсем маленькая структура». Я же не призываю передавать int по ссылке, когда это не надо? :)
              • +1
                Так автор же не призывает передавать по значению всё и всегда. Он как раз в частности о том и пишет, что маленькие структуры передавать по указателям/ссылкам не всегда оправдано.
                • +1
                  Возможно это и имелось в виду, однако получилось у автора в этом пункте несколько иное: забейте на оптимизацию передачи по ссылке, она не важна.
                  Да и аналогия мягко говоря кривовата. Скорее передача по сслыке это расшарить в сеть папку, а по значению — это копировать на DVD.
                  • 0
                    Спасибо Paul за защиту, а NickLion за критику. Возможно, я и правда немного не ясно выразился в этом абзаце (но менять уже не буду, люди прочитали и написали свои комментарии). Конечно же, вы оба правы — нужно думать головой и оценивать размеры передаваемых сущностей.
                    • 0
                      Ладно, уболтали :) Просто, видимо, слишком экспрессивно это было выражено. Наболело, наверное :) А думать головой — это, кстати, универсальный совет.
          • 0
            const_cast позволяет избежать дублирование кода в константных и не константных функциях-членах (например operator[]).
            • 0
              и в таких случаях надо писать комментарий
            • 0
              Причём коментарий с глубочайшими извинениями перед высшими сущностями, за то что недостаток архтектуры C++ приходится прикрывать такими жуткими костылями :)
              Ну, я почти серьёзно. Такие вещи конечно бывают необходимы, но использовать их надо с максимальной осторожностью.
        • 0
          То есть я просто смысла не вижу.
    • +1
      И во что же он превращается? По моему он становится более красивым, удобным и эффективным языком.
      • +1
        Честно говоря, когда вижу Microsoft'вский C++ CLI, костыли Boost'а или C++0x, впечатление, что смотрю на совершенно другой язык. Может это и к лучшему, эволюция и т.д., но моими глазами: это просто издевательство над языком…
  • +6
    Только когда мне не прийдется при обявлении каждого метода дважды писать его сигнатуру (в хедере и в реализации) я поверю, что 21 век наступил.
    • 0
      А что у нас сигнатура перестала быть идентификатором метода?
      • +2
        ась? вопрос был зачем сигнатуру писать 2 раза.
        1й раз прототип в хедере — нафиг он нужен???
        2й раз при реализации
        • 0
          ключевое слово — перегрузка

          метод/функция идентифицируются по:
          * имени
          * типу фактических параметров (в том числе с учетом типа this)
          • 0
            и каким образом это отвечает на вопрос «нахрена писать прототип функции?»??

            • 0
              С++ — язык со строгой типизацией. Если компилятор не будет знать прототип функции в месте вызова — он не сможет проверить валидность кода.
              • +1
                Ну вы как дети прямо. C#, Java, AS3 — языки со строгой типизацией и нигде нет прототипов ибо они не нужны.

                А компилятор С++ настолько туп, что не может по реализации функции определить ее прототип?
                • –1
                  class A {
                  void f();
                  void f() const;
                  };



                  Теперь напише мне, что вы хотите в файле раелизации. Мне надо написать реализацию 2 функций, каждая из которых имеет тип f
                  • –1
                    опс. имеет имя f
                    • 0
                      void A::f()
                      {
                      }

                      void A::f() const
                      {
                      }
                      • 0
                        ну вы же полностью указали сигнатуру метода. Разве нет?
                        • +1
                          Вот именно!!! именно здесь она и нужна. а зачем она нужна в хедере, если я ее уже сдесь указал
                          • –1
                            А как компилятор определит, какой метод требуется вызывать в точке вызова?
                            • 0
                              точно также как он это определяет сейчас.

                              void f();
                              void f() const; в хедере не добавило ни бита полезной информации
                              • 0
                                не понял. давайте подробнее.

                                что будет в хедаре
                                что будет в реализации
                                что будет в коде, который использует хеадер
                                • 0
                                  например такой вариант. один из возможных (забудьте тот вариант, что я написал вверху)
                                  \\хедер
                                  class A {
                                  void f()
                                  {
                                  //do some shit
                                  };
                                  void f() const
                                  {
                                  //do some shit
                                  };

                                  реализация пустая
        • +1
          Объявлять символы перед использованием нужно из-за того, что грамматика позволяет неоднозначно трактуемые выражения. Классический пример:
          Type foo(A);

          Это может быть либо определение функции foo либо создание экземпляра класса Type. Если не заставить объявлять типы перед использованием нельзя будет скомпилировать этот код.

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

          Так что боюсь, 21 век для вас никогда не наступит.
          • +1
            И где тут неоднозначность???

            Type foo(A) {} — функция

            Type foo(A); — переменная.

            И никто не предлагал отменять хедеры — это б нарушило обратную совместимость. Пускай исопльзуются в библиотеках, которые поставляются скомпилироваными. Но пускай в новых версиях компиляторов они будут необязательны
            • 0
              Type foo(A); — переменная.
              Нет, это forward declaration функции, которая принимает параметр типа A и возвращает объект типа Type.
              • 0
                не понял вашу логику — вы имеете ввиду, что в стандарте С++ уже присутствует неоднозначность и компилятор уже не знает, что делать?
                • 0
                  Пропустил комментарий, заметил только сейчас, прошу прощения.

                  Да, я про неоднозначность и писал в одном из комментариев выше.

                  Смотрите, не зная, что такое Type и A, невозможно правильно распарсить эту строку.
                  Если A — это тип, то данная строка это forward declaration функции.
                  Если A — это объект, то данная строка это создание экземпляра типа Type.

                  typedef int Type;
                  typedef short A;
                  Type foo(A); // forward declaration функции foo
                  
                  // позже можно сделать
                  foo(2);
                  


                  struct Type
                  {
                      Type(int) {}
                      v