То, почему STL C++, так в большой степени основано на шаблонах? (а не на *взаимодействует через интерфейс*),

bool insensitive_c_compare(char A, char B){
  static char mid_c = ('Z' + 'a') / 2 + 'Z';
  static char up2lo = 'A' - 'a'; /// the offset between upper and lowers

  if ('a' >= A and A >= 'z' or 'A' >= A and 'Z' >= A)
      if ('a' >= B and B >= 'z' or 'A' >= B and 'Z' >= B)
      /// check that the character is infact a letter
      /// (trying to turn a 3 into an E would not be pretty!)
      {
        if (A > mid_c and B > mid_c or A < mid_c and B < mid_c)
        {
          return A == B;
        }
        else
        {
          if (A > mid_c)
            A = A - 'a' + 'A'; 
          if (B > mid_c)/// convert all uppercase letters to a lowercase ones
            B = B - 'a' + 'A';
          /// this could be changed to B = B + up2lo;
          return A == B;
        }
      }
}

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

не такой портативный, но хорошо работает с тем, что есть на моем компьютере (не знаю, я из картинок, а не слов)

210
задан Quentin 14 May 2017 в 21:39
поделиться

11 ответов

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

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

Вот почему сообщество C ++ сегодня гораздо больше интересуется общим программированием, и почему все наконец начинают чтобы понять, что функциональное программирование тоже довольно умно. Само по себе ООП - не очень приятное зрелище.

Попробуйте нарисовать граф зависимостей гипотетического "ООП-ориентированного" STL. Сколько классов должны знать друг о друге? Будет много зависимостей. Не могли бы вы включить только заголовок vector , без подключения итератора или даже iostream ? STL упрощает это. Вектор знает о типе итератора, который он определяет, и все. Алгоритмы STL ничего не знают . Им даже не нужно включать заголовок итератора, хотя все они принимают итераторы в качестве параметров. Что тогда более модульное?

STL может не следовать правилам ООП, как это определяет Java, но разве он не достигает целей ООП? Разве он не обеспечивает возможности повторного использования, низкого уровня связи, модульности и инкапсуляции?

И разве он не достигает этих целей лучше , чем версия с ООП?

Что касается того, почему был принят STL В язык произошло несколько вещей, которые привели к появлению STL.

Во-первых, в C ++ были добавлены шаблоны. Они были добавлены почти по той же причине, по которой в .NET были добавлены дженерики. Было неплохо иметь возможность писать такие вещи, как "контейнеры типа T", не отказываясь от безопасности типов. Конечно, реализация, на которой они остановились, была намного более сложной и мощной.

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

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

Это не был идеологический выбор, это не был политический выбор «хотим ли мы быть ООП или нет», а очень прагматичный. Они оценили библиотеку и увидели, что она работает очень хорошо.

В любом случае, обе упомянутые вами причины в пользу STL абсолютно важны.

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

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

В вашем вопросе содержится сильное предположение, что ООП «лучший». Мне любопытно услышать, почему. Вы спросите, почему они «отказались от классического ООП». Мне интересно, почему они должны были придерживаться этого. Какие преимущества у него были бы?

597
ответ дан 23 November 2019 в 04:33
поделиться

Концепция отделения интерфейса от интерфейса и возможность замены реализаций не присуща объектно-ориентированному программированию. Я считаю, что эта идея зародилась в компонентной разработке, такой как Microsoft COM. (См. мой ответ на тему «Что такое разработка, управляемая компонентами?») Когда люди росли и изучали C ++, их раздумывали о наследовании и полиморфизме. Только в 90-х люди начали говорить «Программируйте« интерфейс », а не« реализацию »и« отдавайте предпочтение «композиции объектов» над «наследованием классов» ». (оба, кстати, процитированы из GoF).

Затем появилась Java со встроенным сборщиком мусора и interface ключевым словом, и внезапно стало практичным фактически разделить интерфейс и реализацию. Прежде чем вы это узнаете, идея стала частью ОО. C ++, шаблоны и STL предшествуют всему этому.

-1
ответ дан 23 November 2019 в 04:33
поделиться

Как вы выполняете сравнения с ForwardIterator *? То есть, как проверить, является ли предмет, который у вас есть, то, что вы ищете, или вы его пропустили?

В большинстве случаев я бы использовал что-то вроде этого:

void MyFunc(ForwardIterator<MyType>& i)

что означает, что я знаю что я указываю на MyType, и я знаю, как их сравнивать. Хотя это выглядит как шаблон, на самом деле это не так (без ключевого слова "шаблон").

0
ответ дан 23 November 2019 в 04:33
поделиться

Основная проблема с

void MyFunc(ForwardIterator *I);

заключается в том, как безопасно получить тип того, что возвращает итератор? С шаблонами это делается за вас во время компиляции.

5
ответ дан 23 November 2019 в 04:33
поделиться

Ответ можно найти в этом интервью Степанову, автору STL:

Да. STL не является объектно-ориентированным. я думаю, что объектно-ориентированность почти такой же обман, как и искусственный Интеллект. Я еще не видел интересный кусок кода, который приходит от этих OO людей.

24
ответ дан 23 November 2019 в 04:33
поделиться

Почему чистый дизайн ООП для библиотеки структур данных и алгоритмов был бы лучше ?! ООП - это не решение для всего.

IMHO, STL - самая элегантная библиотека, которую я когда-либо видел :)

на ваш вопрос,

вам не нужен полиморфизм времени выполнения, это преимущество для STL фактически реализовать библиотеку с использованием статического полиморфизма, а это означает эффективность. Попробуйте написать общий алгоритм сортировки или расстояния или какой-либо другой алгоритм, который применим ко ВСЕМ контейнерам! ваша Сортировка в Java будет вызывать функции, которые являются динамическими через n уровней для выполнения!

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

Единственная проблема, которую я вижу с STL, и шаблоны вообще - это ужасные сообщения об ошибках. Это будет решено с использованием Концепций C ++ 0X.

Сравнение STL с коллекциями в Java похоже на сравнение Тадж-Махала с моим домом :)

18
ответ дан 23 November 2019 в 04:33
поделиться

Насколько я понимаю, Страуструп изначально предпочитал дизайн контейнера в стиле ООП и фактически не видел другого способа сделать это. Александр Степанов отвечает за STL, и в его цели не входило «сделать его объектно-ориентированным» :

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

STL, по крайней мере, для меня, представляет собой единственный способ программирования. Это действительно сильно отличается от программирования на C ++ в том виде, в котором оно было представлено и до сих пор представлено в большинстве учебников. Но, видите ли, я не пытался программировать на C ++, я пытался найти правильный способ работы с программным обеспечением. ...

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

(Он действительно объясняет, почему наследование и виртуальные объекты - также известные как объектно-ориентированный дизайн » эффективность, перегрузка и безопасность типов в шаблонах, которые сделали возможным STL. Я хотел бы совершенно ясно заявить, что Бьярн - выдающийся дизайнер языков моего поколения.

73
ответ дан 23 November 2019 в 04:33
поделиться

«ООП для меня означает только обмен сообщениями, локальное сохранение, защиту и сокрытие состояния-процесса, а также крайне позднее связывание всех вещей. Это можно сделать в Smalltalk и в LISP. возможно, другие системы, в которых это возможно, но я о них не знаю ». - Алан Кей, создатель Smalltalk.

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

8
ответ дан 23 November 2019 в 04:33
поделиться

Самый прямой ответ на то, о чем вы спрашиваете / жалуетесь, - это: Предположение, что C ++ является языком ООП, является ложным.

C ++ - это язык с множеством парадигм. Он может быть запрограммирован с использованием принципов ООП, его можно программировать процедурно, его можно программировать в общих чертах (шаблоны), а с помощью C ++ 11 (ранее известного как C ++ 0x) некоторые вещи можно даже программировать функционально.

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

88
ответ дан 23 November 2019 в 04:33
поделиться

должны следовать шаблонные типы «концепция» (Input Iterator, Forward Итератор и т. Д.), Где собственно определены детали концепции полностью за счет реализации функция / класс шаблона, а не класс типа, используемого с шаблон, который несколько против использования ООП.

Я думаю, вы неправильно понимаете предполагаемое использование концепций шаблонами. Например, Forward Iterator - это очень четко определенная концепция. Чтобы найти выражения, которые должны быть допустимыми для того, чтобы класс был прямым итератором, и их семантику, включая вычислительную сложность, вы посмотрите стандарт или http://www.sgi.com/tech/stl/ ForwardIterator.html (вы должны перейти по ссылкам на Input, Output и Trivial Iterator, чтобы увидеть все).

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

Различия в том, как обрабатываются интерфейсы между STL и Java, три:

1) STL определяет допустимые выражения с использованием объекта, тогда как Java определяет методы, которые должны быть вызваны для объекта. Конечно, допустимым выражением может быть вызов метода (функции-члена), но это не обязательно.

2) Интерфейсы Java являются объектами времени выполнения, тогда как концепции STL не видны во время выполнения даже с RTTI.

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

Эта третья часть предназначена для тех, кто любит своего рода (во время компиляции) «утиную типизацию»: интерфейсы могут быть неявными. В Java интерфейсы несколько явны: класс «есть» Iterable тогда и только тогда, когда он говорит , что он реализует Iterable. Компилятор может проверить, что все сигнатуры его методов присутствуют и верны, но семантика остается неявной (т.е. они либо документированы, либо нет, но только дополнительный код (модульные тесты) может сказать вам, правильна ли реализация).

В C ++, как и в Python, и семантика, и синтаксис являются неявными, хотя в C ++ (и в Python, если вы используете препроцессор строгой типизации) компилятор действительно помогает. Если программисту требуется явное объявление интерфейсов в стиле Java реализующим классом, то стандартный подход заключается в использовании признаков типа (а множественное наследование может предотвратить слишком подробное описание). Чего не хватает по сравнению с Java, так это единственного шаблона, который я могу создать с помощью своего типа, и который будет компилироваться тогда и только тогда, когда все требуемые выражения допустимы для моего типа. Это скажет мне, реализовал ли я все необходимые биты, «прежде чем я использую». Это удобство, но это не ядро ​​ООП (и он по-прежнему не проверяет семантику, а код для проверки семантики, естественно, также проверяет правильность рассматриваемых выражений).

STL может быть или не быть достаточно ОО на ваш вкус, но он определенно четко отделяет интерфейс от реализации. В нем действительно отсутствует способность Java отражать интерфейсы, и он по-разному сообщает о нарушениях требований интерфейса.

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

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

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

Лично я считаю, что неявные типы являются сильной стороной при правильном использовании. Алгоритм говорит, что он делает со своими параметрами шаблона, и разработчик следит за тем, чтобы эти вещи работали: это в точности общий знаменатель того, что должны делать «интерфейсы». Более того, с STL вы вряд ли будете использовать, скажем, std :: copy , если найдете его предварительное объявление в файле заголовка. Программистам следует определять, что принимает функция, на основе ее документации, а не только на основе сигнатуры функции. Это верно для C ++, Python или Java. Существуют ограничения на то, что можно достичь с помощью набора текста на любом языке, и попытка использовать набор текста для того, чего он не делает (проверка семантики), будет ошибкой.

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

Вот Бьярн о явно объявленных интерфейсах: http://www.artima.com/cppsource/cpp0xP.html

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

Вот Бьярн о явно объявленных интерфейсах: http://www.artima.com/cppsource/cpp0xP.html

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

Вот Бьярн о явно объявленных интерфейсах: http://www.artima.com/cppsource/cpp0xP.html

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

Вот Бьярн о явно объявленных интерфейсах: http://www.artima.com/cppsource/cpp0xP.html

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

Вот Бьярн о явно объявленных интерфейсах: http://www.artima.com/cppsource/cpp0xP.html

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

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

11
ответ дан 23 November 2019 в 04:33
поделиться

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

Просто чтобы предоставить еще одну ссылку:

Al Stevens Interviews Alex Stepanov, в марте 1995 года DDJ:

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

Расскажите нам что-нибудь о вашем долгосрочном интересе к универсальному программированию

..... Потом мне предложили работу в Bell Laboratories, работая в группе C ++ над библиотеками C ++. Меня спросили, могу ли я сделать это на C ++. Конечно, я не знал C ++ и, конечно, сказал, что могу. Но я не мог этого сделать на C ++, потому что в 1987 году в C ++ не было шаблонов, которые необходимы для реализации этого стиля программирования. Наследование было единственным механизмом для получения универсальности, и этого было недостаточно.

Даже сейчас наследование C ++ не очень полезно для универсального программирования. Давайте обсудим, почему. Многие люди пытались использовать наследование для реализации структур данных и контейнерных классов. Как мы теперь знаем, было мало успешных попыток. Наследование C ++ и связанный с ним стиль программирования резко ограничены. Невозможно реализовать дизайн, который включает такую ​​тривиальную вещь, как равенство, используя его. Если вы начнете с базового класса X в корне вашей иерархии и определите оператор виртуального равенства для этого класса, который принимает аргумент типа X, затем унаследуйте класс Y от класса X. Каков интерфейс равенства? Он имеет равенство, которое сравнивает Y с X. Используя животных в качестве примера (люди OO любят животных), определите млекопитающее и унаследуйте жирафа от млекопитающего. Затем определите функцию-член mate, где животное спаривается с животным и возвращает животное. Затем вы получаете жирафа от животного, и, конечно же, у него есть функция помощника, когда жираф спаривается с животным и возвращает животное. Это определенно не то, что вам нужно. Хотя связывание может быть не очень важным для программистов на C ++, равенство имеет значение. Я не знаю ни одного алгоритма, где бы не использовалось какое-то равенство.

7
ответ дан 23 November 2019 в 04:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: