В предыдущей заметке речь шла о массивах как прототипе и прародителе контейнеров. Теперь дошла очередь до собственно контейнерных классов и поддерживающих их библиотек.

Под термином библиотека стандартных шаблонов (STL, Standard Template Library) понимают набор интерфейсов и компонентов, первоначально разработанных Александром Степановым, Менг Ли и другими сотрудниками AT&T Bell Laboratories и Hewlett-Packard Research Laboratories в начале 90-х годов (хотя и позже ещё весьма многие приложили руку к тому, что стало на сегодня стандартным компонентом C++). Далее библиотека STL перешла в собственность компании SGI, а также была включена как компонент в набор библиотек Boost. И наконец библиотека STL вошла в стандарты C++ 1998 и 2003 годов (ISO/IEC 14882:1998 и ISO/IEC 14882:2003) и с тех пор считается одной из составных частей стандартной библиотек C++.

Стандарт не называет эту часть библиотеки STL, но эту хронологию хорошо бы учитывать, разбираясь с какой версией компилятора, языка и литературы вы имеете дело — в процессе сокращения HP STL до размеров, подходящих для стандартизации, часть алгоритмов и функторов выпали из состава библиотеки, а кое-что, со временем, и добавляется (например, расширение набора переопределенных прототипов некоторых методов контейнеров). По тексту будет использоваться традиционное название STL только чтобы было ясно какую часть стандартной библиотеки C++ мы имеем в виду.

Первоначальной целью STL (это хорошо видно из хронологии комментариев в заголовочных файлах) было создание боле гибкой модели регулярных контейнеров по сравнению с массивами и обобщение на них некоторых широко используемых алгоритмов (таких как поиск, сортировка и некоторых других). Но затея оказалась плодотворнее первоначальных намерений, и была существенно расширена. STL вводит ряд понятий и структур данных, которые почти во всех случаях позволяют сильно упростить программный код. Вводятся следующие категории понятий:

  1. Контейнер — способ хранения набора объектов в памяти.
  2. Итератор — средство доступа к содержимому отдельных объектов в контейнере.
  3. Алгоритм — определение наиболее стандартных вычислительных процедур на контейнерах.
  4. Адаптер — адаптация основны категорий для обеспечения наиболее употребляемых интерфейсов (таких как стек или очередь).
  5. Функтор (функциональный объект) — сокрытие функции в объекте для использования её другими категориями.

Библиотека STL — это весьма обширная область. Её описанию посвящены целые отдельные книги. Здесь же, в силу достаточно начального уровня знакомства, ограниченности объёма и следованию объявленных ранее целей, будет рассмотрена техника использования STL на интуитивно ясных примерах. Синтаксис STL основан на использовании таких синтаксических конструкций языка C++ как шаблоны (templates) классов и шаблоны функций. Но для успешного применения техники STL совсем не обязательно глубокое понимание техники templates (это может прийти позже).

Центральным понятием STL, вокруг которого крутится всё остальное, это контейнер (ещё используют термин коллекция). Контейнер — это набор некоторого количества обязательно однотипных элементов, упакованных в контейнер определённым образом. Простейшим прототипом контейнера в классическом языке C++ является массив. Тот способ, которым элементы упаковываются в контейнер и определяет тип контейнера и особенности работы с элементами в таком контейнере. STL вводит целый ряд разнообразных типов контейнеров, основные из них:

  • последовательные контейнеры — вектор (vector), двусвязный список (list), дэк (deque);
  • ассоциативные контейнеры — множества (set и multiset ), хэш-таблицы (map и multimap);
  • псевдо контейнеры — битовые маски (bitset), строки (string и wstring), массивы (valarray);

Мы рассмотрим на примерах использования последовательно основных из них, переходя от более простых к сложным. Простейшим типом контейнеров является вектор. Близким прототипом вектора является массив С++, но размер вектора может динамически изменяться в любое время операциями добавления (метод push_back()) или удалении (например, метод pop_back()) элемента. Так же, как и для массива, мы можем обратиться к произвольному элементу вектора операцией индексации [ n ]. То, что уже сказано — это уже и есть первый, поверхностный слой познаний о vector, но который достаточен для ого, чтобы позволить начать с ним работать на аналогиях того, как мы работаетм с традиционными массивами:

#include <iostream> 
#include <vector> 
#include <climits> 
using namespace std; 
 
void put( const vector<float>& v ) { 
   cout << "capacity=" << v.capacity() 
        << ", size=" << v.size() << " : "; 
   for( unsigned i = 0; i < v.size(); i++ ) 
      cout << v[ i ] << " "; 
   cout << endl; 
} 

vector<float>& operator +=( vector<float>& v, unsigned n ) { 
   float last = v[ v.size() - 1 ]; 
   for( unsigned i = 0; i < n; i++ ) 
      v.push_back( last + i + 1 ); 
   return v; 
} 

int main( void ) { 
   float data[] = { 1., 2., 3., 4., 5., 6., 7. }; 
   int n = sizeof( data ) / sizeof( data[ 0 ] ); 
   vector<float> array( data, data + n ); 
   cout << "max_size=" << array.max_size() 
        << "  ((INT_MAX+1)/2)=" << ( (unsigned)INT_MAX + 1 ) / 2 << endl; 
   put( array ); 
   put( array += 2 ); 
   put( array += 6 ); 
}

Описание vector<float> (это и есть упоминавшийся ранее template в описании класса) объявляет в коде <b>объект</b>: вектор
элементов типа float. Далее мы видим такие методы класса vector как max_size() — максимально возможная длина векторов вообще (константа реализации), size() — текущий размер (число элементов) вектора, capacity() — текущая ёмкость вектора, максимальное число элементов, которое может быть помещено в вектор в текущем его размещении. Выполнение этого фрагмента даст что-то примерно следующее (детали могут различаться в зависимости от реализации):

$ ./vect1 
max_size=1073741823  ((INT_MAX+1)/2)=1073741824 
capacity=7, size=7 : 1 2 3 4 5 6 7 
capacity=14, size=9 : 1 2 3 4 5 6 7 8 9 
capacity=28, size=15 : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 

Здесь видно достаточно интересное поведение вектора (в этом и его смысл): как только при добавлении очередного элемента вектора его ёмкости становится недостаточно для ещё одного элемента, делается новое размещение вектора, резервируя для него удвоенную ёмкость (с запасом, чтобы следующее же добавление нового элемента не потребовало тут же нового переразмещения).

Примечание: Показанное выше удвоение ёмкости вектора при переразмещении — это характерное поведение для реализации библиотек компилятора GCC. Но точный алгоритм резервирования ёмкости под будущие элементы стандарт оставляет на волю реализатора, поэтому на него нельзя рассчитывать, и описан он здесь только для качественного понимания картины (реализации Visual Studio ведут себя по-другому, резервируя только небольшой избыток… это вы изучите сами).

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

Таким образом мы получили эквивалент массива C++, размер которого (size()) динамически меняется в произвольных пределах от нескольких единиц до миллионов элементов. Обратим внимание (это очень важно), что увеличение размера вектора достигается ни в коем случае не индексацией за пределы его текущего размера, а «заталкиванием» (метод push_back()) нового элемента в конец вектора (симметрично, метод pop_back() выталкивает последний элемент из массива и уменьшает его size()). Другой способ изменить размер вектора — это сразу вызвать методы resize() под нужный размер. Именно потому, что размер вектора, в отличие от массива, может динамически меняться, для вектора предусмотрено 2 разных способа индексации: как операция [ i ] и как метод-функция at( i ). Они различаются: метод at() проверяет текущий размер вектора size(), и при индексации за его границу возбуждает исключение. Напротив, операция индексации не проверяет границу, что небезопасно, но зато это быстрее. Метод at() позволяет нам контролировать выход за границы вектора и либо квалифицировать это как логическую ошибку, либо корректировать текущий размер контейнера под потребность, как в вот таком фрагменте (здесь попыток доступа вдвое больше, чем реально выполненных операций):

int main( void ) { 
   vector<int> nums; 
   for( int i = 0; i < 10; ) { 
      try { 
         nums.at( i ) = i;    // vector::at throws an out-of-range 
         i++; 
      } 
      catch( const out_of_range& ) { 
         cout << i << " "; 
         nums.resize( i + 1 ); 
      } 
   } 
   cout << endl << nums.size() << endl; 
} 

$ ./vect7 
0 1 2 3 4 5 6 7 8 9 
10 

Стандарт C++11 привносит дополнительные выразительные средства, такие, например, как списки инициализации и выводимость типов, которые намного упрощают работу с контейнерами (и даже делают ненужными старые привычные приёмы записи). Вот как может описываться матрица, когда одновременно описываются её а). конфигурация (квадратная, хотя может быть прямоугольная и даже треугольная), b). размерность (3х3) и c). инициализирующие значения:

void print( const vector< vector<float> >& m ) { 
   for( auto &row : m ) { 
      for( auto x : row ) 
         cout << x << ' '; 
      cout << endl; 
   } 
} 

void trans( vector< vector<float> >& m ) { 
   for( unsigned i = 0; i < m.size(); i++ ) 
      for( unsigned j = i + 1; j < m[ i ].size(); j++ ) { 
         float tmp = m[ i ][ j ]; 
         m[ i ][ j ] = m[ j ][ i ]; 
         m[ j ][ i ] = tmp; 
      } 
} 

int main( void ) { 
   vector< vector<float> > matrix = { 
      { 1, 2, 3 }, 
      { 4, 5, 6 }, 
      { 7, 8, 9 } 
   }; 
   print( matrix ); 
   cout << "---------" << endl; 
   trans( matrix ); 
   print( matrix ); 
} 

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

$ ./vect6 
1 2 3 
4 5 6 
7 8 9 
--------- 
1 4 7 
2 5 8 
3 6 9 

Примечание: В рамках того, что мы уже знаем о векторах, возникает иногда вопрос: а как строго должен определяться тип возвращаемого size() результата (чтобы избежать зависимости от платформы) и, соответственно, любых переменных циклов, оперирующих с размером вектора? Временами от блюстителей чистоты синтаксиса следует ответ, что это должен быть size_t, и этот ответ — неверный (тем более, что для многих платформ size_t и определяется как unsigned int). Если вы захотите записать абсолютно строгого определение типа size() вектора, то строку в примере выше следует записать вот так:

for( vector<float>::size_type j = i + 1; j < m[ i ].size(); j++ ) { ...

Или, полагаясь на выводимость типов C++11, вот так:
for( auto j = i + 1; j < m[ i ].size(); j++ ) { ...

Отметив здесь этот тонкий нюанс (приняв к сведению), мы не станем его применять далее, во избежания лишней громоздкости примеров, а будем использовать для размерностей просто unsigned.

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

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


  1. gbg
    03.03.2016 15:31
    +17

    И снова с нами куча дидактических ошибок:

    • вы продолжаете использовать всякую ерунду в качестве индексов массива. Уж коль скоро сослались на container::size_type, так и используйте. Иначе как вы потом объясните людям, что их программа отваливается на 64 разрядной системе после второго гигабайта? Привычка садить int везде — вырабатывается быстро, искореняется — с трудом. Даже семинары с коктейлями не помогают.

    • STL, векторы, и НИ ОДНОГО итератора. Просто выкинута из обсуждения вся прелесть ортогонализации алгоритмов от структур данных. Зато в теги слово "итераторы" вы засунули. Почему итераторы лучше индексов? Хотя бы потому, что итератор можно сделать константной ссылкой, что гарантирует невозможность испортить массив. Ошибка программиста (опечатка, например), которая нарушает здравый смысл (попытка записи в массив при его распечатке, например), при наличии константной ссылки будет отловлена еще на этапе компиляции.

    • самопальный и громоздкий обмен вместо std::swap(). Использование std::swap() — наиболее правильно и переносимо. На какой-то платформе std::swap развернется в xor-swap, на какой-то — в специальную инструкцию вроде xchg, где-то будет замена через временную переменную… но нет, вы всадили единственный способ, не продемонстрировав хороший тон.

    • снова перемешаны подходы из C и С++. Если в C под всякое преобразование типов нужна конструкция "(новый_тип)значение", что порождает путаницу (а что будет, если сделать (double)i, где i — переменная типа int? Будет создано новое значение типа double, или double побитово натянется на int? А ничего что размер в битах разный?). Специально, чтобы путаницу исключить, в С++ есть именованные преобразования, когда становится кристально ясно, что происходит. Почему бы сразу не учить хорошему подходу, который снижает показатель WTF в секунду?

    • путаница стилей C и C++ относится и к макросу INT_MAX. Для этого в C++ есть std::numeric_limits

    • вы не используете const, не демонстрируете учащимся хороший тон (const должен быть на всем, что не меняется!)

    Напоминаю про кодстайл:

    • дурацкий кодстайл Кернигана и Ричи, также известный как "египетские скобки", также известный как "строки на мониторе выдаются строго по талонам" — не делает C++ доступнее для изучающих язык новичков. Если скобки выровнять по вертикали, можно легко отлавливать границы блока простым пробегом глаз по вертикали, без необходимости носиться по концам всех строк, в поисках парной скобки. Даже подсветка от IDE не спасает.


    1. Olej
      03.03.2016 16:46
      -8

      И огород
      На случай если Крот прийдёт

      ;-)


    1. JIghtuse
      03.03.2016 17:02
      +4

      Подписываюсь под каждым словом. Такие статьи вредны?. Замечу также, что не увидел контейнеров, которые в заголовок вынесены. Один только vector, без упоминания хотя бы array там, где он отлично бы вписался:

         float data[] = { 1., 2., 3., 4., 5., 6., 7. }; 
         int n = sizeof( data ) / sizeof( data[ 0 ] );

      В настоящее время очень редко нужна возможность итерироваться по контейнеру индексом. Практически всё можно выразить алгоритмами и итераторами. В статье же не то что Your Father's C++, а какой-то Your Grandfather C++. Да и вообще наполовину C.


      1. maksqwe
        03.03.2016 17:09
        +2

        В этом ключе советую авторам взглянуть на Range Library
        https://github.com/ericniebler/range-v3
        Очень сильно расширяет возможности контейнеров и алгоритмов. Пока предлагается на рассмотрение комитету в стандартную библиотеку:
        https://ericniebler.github.io/std/wg21/D4128.html

        Хотелось бы увидеть статьи про эти новые вещи.


        1. Olej
          03.03.2016 17:33
          -4

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


      1. Olej
        07.03.2016 13:18
        -4

        Подписываюсь под каждым словом. Такие статьи вредны?. Замечу также, что не увидел контейнеров, которые в заголовок вынесены. Один только vector, без упоминания хотя бы array там, где он отлично бы вписался:

        Если бы вы ещё и читали, а не только писали, да ещё внимательно, то видели бы, что это только малая начальная часть серии заметок, следующая из которых как раз и предназначалась постепенному и естественному переходу от индексов к итераторам.
        (Но куда уж нам: чукча, он, как известно, писатель...)
        Так что в следующий раз, «подписываюсь под каждым словом» очередного пустобрёха, читайте внимательно в адрес чего там вы подписываетесь.


      1. Olej
        07.03.2016 13:20
        -3

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

        И кто же это вам рассказал столько удивительных глупости про «настоящее время»? Использование индексации или итераторов — две независимые формы выразить зачастую одно и то же. И, опять же, если бы вы ещё и читали то, что комментируете (кроме того чтобы спешить писать), то там бы вы отчётливо прочли про круг программистов и задач, с которыми обсуждались и на кого рассчитаны эти заметки: численный анализ, обработка временных рядов, сигналов, ортогональные преобразования, вэйвлеты и авто-регрессионный спектральный анализ… Это области вычислений, где алгоритмы десятилетиями выражались и публиковались исключительно в терминологии массивов и индексов: вектора, матрицы, свёртки, фильтрации. И переписывать их через итераторы часто просто нецелесообразно («из пустого в порожнее»), а когда и нужно, то делать это следует плавным, мягким переходом от индексов к итераторам.


      1. Olej
        07.03.2016 13:23
        -5

        Такие статьи вредны?.

        Но если вы так определённо считаете, что таки «вредны?», то я, конечно, не смею не прислушаться к столько авторитетному мнению и не стану продолжать...

        Мысли вслух…
        Я там ненароком посмотрел всякие плюсы там, минусы … хотя вся эта ваша хабрахабровская лобода, шелупень и рюшечки — лайки, дислайки, кармы, рейтинги …: мне это глубоко всё до задницы. Но вот при ~10К просмотров, тех, кто не поленился отметиться лайком вдвое больше тех, кто отметился минусом. И это при том ещё, что минусят часто и не читая даже текст, испытывая одну только неприязнь к автору … (как это начнётся сейчас после моих вам ответов).
        Но если бы даже и не вдвое, и если бы даже вообще не больше — просто есть те, кому такое обсуждение могло бы быть интересно и полезно. Но те, кому это вправду интересно (и читать и обсуждать), найдут продолжения в других местах, где эту серию, с разными вариациями, охотно публикуют.
        А с вами, друг мой любезный, мы на этом и распрощаемся.

        Идёт врачебный обход:
        — До свидания, Иванов. До завтра, Петров. А вы, Сидоров — прощайте.

        … и с 8-м марта тебя, убоище ;-)


        1. Olej
          07.03.2016 18:31
          -5

          (как это начнётся сейчас после моих вам ответов).

          Ну так вот же ж оно: как начали исподтишка гадить, шавки! ;-)
          Но вякнуть вслух — не смеют ...


          1. Olej
            08.03.2016 22:22
            -5

            Но заткнулись то как славно!

            Сидят все "эксперты" тихо-тихо… — как мышки ...

            И только минусят, минусят и минусят своими паскудными пальчиками. ;-) ;-) ;-)


            1. Chaos_Optima
              09.03.2016 18:08
              +3

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


              1. Olej
                09.03.2016 18:36
                -4

                Минус от гопоты — это как медаль. ;-)


                1. Chaos_Optima
                  09.03.2016 18:43
                  +3

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


                  1. Olej
                    09.03.2016 19:35
                    -4

                    А это как вам будет угодно… только меня это мало занимает.


    1. Amomum
      03.03.2016 20:25
      +5

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

      Заголовок спойлера
      Но сам я предпочитаю allman style.


  1. gbg
    03.03.2016 15:41
    +6

    И я только что увидел еще одну огромную гадость — вы умудрились переобозначить оператор "присвоение с суммированием" (+=), исказив его смысл.

    Это — одна из самых коварных ошибок программиста на C++, потому как ведет к разночтениям ("Что это значит? Увеличить все в массиве на X?!). Вы снова вместо того, чтобы показать хороший тон (вполне очевидное и ясное push_back) увеличили количество WTF.


    1. Olej
      07.03.2016 12:54
      -4

      я только что увидел еще одну огромную гадость

      До чего же сильно я люблю этих диванных стратегов!!!
      (по преимуществу выползков из начинающих вузовских пЫдагогов).

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

      Мне как-то недосуг обсуждать здесь все высказанные титаном мысли… «аргуенты», с позволения сказать, относящиеся больше к категории бла-бла-бла, и не более того…
      Но в отношении некоторых этих озарений я таки … не могу себе отказать в удовольствии постебаться. (Тем более, что согласно интриге, я специально долго молчал, чтоб дать возможность всласть высказаться всем.)

      Ну прежде всего, давным-давно известное: Feci quod potui, faciant meliora potentes
      Вас, gbg, не устраивают эти скромные заметки ни по стилю ни по содержанию? Ну так это ж и есть самое что ни есть время выйти на арену силачам мысли и акробатам духа, и сделать то же самое, но гораздо лучше!


    1. Olej
      07.03.2016 12:59
      -5

      я только что

      Ну что? Поехали в стёб? ;-)

      Иначе как вы потом объясните людям, что их программа отваливается на 64 разрядной системе после второго гигабайта?

      Ваше намерение объявить в программе вектор длиной больше 1 миллиарда — меня искренне повеселило… просто повергло в божественный трепет (вы кстати, думаете что пишете? Или просто пишете что думаете?).

      Безумству храбрых поем мы славу!
      Безумство храбрых — вот мудрость жизни

      Я даже начал благодаря вам вновь обретать совсем уже, было, потерянную веру в человечество...

      итерируя массив всякой ерундой вроде unsigned (хорошо еще не int), когда для этого есть size_t

      Где-то там я оговорился, что пишу для «цифровиков», занимающихся обработкой сигналов. Мне очень любопытно было бы посмотреть, как бы вы их упорно наставляли везде для индексации массивов (буферов, окон сглаживания, импульсных характеристик фильтров..., весьма ограниченного, в общем, размера: 128, 512, 1024 …), использовать не любый им и привычный int, а size_t или, ещё лучше, container::size_type.

      Но с куда ещё большим удовольствием я бы выслушал на (или в?) какой орган бы они вас послали за такие вот ваши советы.
      Я уважаю вашу болезненно-маниакальную … «мечту идиота» (здесь ничего личного — это просто такое идиоматическое выражение русского языка) настаивать для индексации повсеместно и исключительно посредством size_t … но нужно же и лицо как-то беречь от грубой силы, когда такое советуешь?


    1. Olej
      07.03.2016 13:09
      -4

      проигнорировав даже элементарные проверки входных параметров

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

      путаница стилей C и C++ относится и к макросу INT_MAX. Для этого в C++ есть std::numeric_limits

      А даже если путаница стилей … и есть std::numeric_limits? Ну и что из того? Для многих вещей есть множество способов выполнить одно и то же. А относительно смешения стилей, так вас никак не смущает то:
      … что 20% или 30% заголовочных файлов C++ ( и мн.др.) — это только отсылка к заголовочным файлам C API (<stdlib.h> и т. д.);
      … что большая часть библиотек C++ API — это всего лишь обёртки над POSIX API;
      … что скомпилированный C++ код исполняется только совместно с библиотекой libc.so (стандартная библиотека C, по-народному), и что в отсутствии этой библиотеки, или если путь к ней не удастся найти, все приложения C++ просто окажутся неработоспособными (это я про нормальные операционные системы, не про Windows);

      А в отношении стилей, путаницы и прочей шелупени, я могу, друг мой gbg, вот что вам сказать: в программной инженерии есть только 2 стиля: либо тяжело строгать работающий код под реальные внедрения, либо … «строть из себя целку»: кода не производя, но мечтательно рассуждая о тотальной индексации с size_t и о том, где скобочки элегантнее будут выглядеть.


    1. Olej
      07.03.2016 13:15
      -4

      не продемонстрировав хороший тон.

      Молодой человек (судя по манерности ваших публикаций я имею все основания так считать), я не учитель бальных танцев, чтобы преподавать хороший тон. Я готов обсуждать технические, инженерные предметы — и понимание этой разницы очень важно.
      Демонстрировать хороший тон «без дидактических ошибок» — это я оставляю вам.

      Вы слишком много начитались дурных книжек про «идеальный код», и приняли их слишком близко к сердцу.

      вы не используете const, не демонстрируете учащимся хороший тон (const должен быть на всем, что не меняется!)

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

      Напоминаю про кодстайл:

      Ну, тут вообще порадовал… Этого я так ждал…
      Мой юный друг, мне ваш кодстайл, или то, что вы думаете, что под ним понимаете — искренне по-хер (это правда, а правда ведь не бывает грубой, так ведь?). Если написанное мной так глубоко ранит ваше нежное эстетическое чувство и клокочущее гражданское достоинство, то … я скорблю вместе с вами: потому как я писал, пишу и буду писать только так, как привык и как считаю удобным для себя, любимого.
      Как там у Иосифа Бродского на этот счёт?

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

      А ваше ущемлённое достоинство может спасти теперь только одно — неукоснительное следование правилу «трёх не» … ну, в данном конкретном случае: плюнул — повернулся — и не читай.

      P.S. Пыжиться отвечать на мои наблюдения не надо...

      Не говори никому. Не надо.

      Потому что я всё-равно не стану это читать, да и не буду я дебатировать о вещах, которые для меня давно уже очевидны.


  1. izvolov
    04.03.2016 10:33
    +2

    Менг Ли — женщина.


    1. Olej
      06.03.2016 14:20
      -2

      Спасибо — единственное умное замечание ;-). Исправил.