LINUX.ORG.RU

Какую книгу по C++ выбрать?

 ,


5

2

Какую книгу по с++ выбрать

  1. Герберт Шилдт. C++ для начинающих. Шаг за шагом
  2. Стивен Прата. Язык программирования C++. Лекции и упражнения
  3. Предложите свой вариант (учил джаву, с++ смотрел видео ну можно сказать не с нуля учу)

Перемещено hobbit из general

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

Я тоже исхожу из того, что есть несколько стадий, но стадии отличаются от ваших.

  1. Закончил заниматься самообразованием (с участием ВУЗа или без - не важно).
  2. Устроился на работу джуном - за год освоился с процессами, инструментарием, начал въезжать в предметную область проекта, попутно освоив базовый набор языка программирования.
  3. Набив некоторое количество шишек на сегфолтах, мемликах и прочих экспешнах продолжил самообучение и освоил новый набор механизмов языка.
  4. Повторяем пункт 2 итерация за итерацией.

В такой схеме - это работает. Проверено не однократно, проверено на многих людях.

том же С++ на первой стадии можно знать даже больше, чем на второй.

Я не считаю знание из учебника за знание по двум причинам:

  1. Знания вылетают из головы с большей скоростью, чем проникают туда.
  2. Теоретически сложные конструкции из учебника не применяются на практике, потому что нет опыта их применения за пределами вычурного школьного примера. Не стреляет паттерн-матчинг "а, да это же можно сделать через RAII" у человека знающего про RAII из учебника, но не работавшего со сколько-нибудь сложной кодовой базой. Не распознаётся шаблон. Умение применять все "сложные" фичи из С++ к реальному коду требуют практики работы с кодом хоть сколько-нибудь продолжительное время. На студенческих лабах этот навык не приобретается.
LamerOk ★★★★★
()
Ответ на: комментарий от soomrack

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

А при чём тут "физическая" реализация контейнера? Модель памяти Си / С++ описана в стандарте на Си. Ни о реализации контейнера, ни тем более о железе тут никто не говорит.

Невозможно объяснить array/vector без объяснения семантики адресной арифметики.

Прекрасно можно. Зачем там говорить про адреса?

И как вы будете объяснять результат выражения:

    std::vector<int> v = { 1, 2, 3};
    auto i = v[v.size()];

?

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

Я тоже исхожу из того, что есть несколько стадий, но стадии отличаются от ваших.

Ваши стадии применимы разве что к войтишником.

Я же описал то, что через что проходил сам, в том числе и через много лет после окончания ВУЗа. ИМХО, даже у опытного программиста, изучающего новый для себя язык (особенно если он в рамках новой парадигмы), будут именно такие стадии. Только джуном он не будет.

Я не считаю знание из учебника за знание по двум причинам:

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

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

По моему опыту – это проблемы обучения. Таким паттернам можно и нужно учить, особенно когда обучение ведет преподаватель.

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

Я полагаю, что это допущение ложно.

Вообще-то я выше говорил:

Я бы сказал, что невозможно знать C++ целиком, мало кто способен на это. Да и не нужно. Так что при обучении правильнее бы ставить вопрос о том, чтобы человек ограниченно знал несколько подмножеств С++ (базу про классы, базу про шаблоны, базу про исключения и exception safety) и уверенно это применял.

«Ограниченно знать» и «уверенно применять» не зря написано. Про шаблоны можно знать минимум, но уметь этим минимумом пользоваться. Скажем, человек должен быть способен написать собственный шаблонный min(x, y) или собственный крайне простой Point3D<T>. Пусть даже и без static_assert-ов внутри.

Как раз привычка расставлять static_assert-ы должна прийти позже. Как и понимание где и когда это следует делать.

Если же такого уверенного применения минимальных знаний нет, значит изучение не заверилось. Или завершилось с отрицательным результатом.

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

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

Я просто эту стадию за стадию не рассматриваю. Это просто некое предусловие к началу обучения.

По моему опыту – это проблемы обучения. Таким паттернам можно и нужно учить, особенно когда обучение ведет преподаватель.

Если же такого уверенного применения минимальных знаний нет, значит изучение не заверилось. Или завершилось с отрицательным результатом.

Как вы себе это представляете в объёме нескольких лаб в отрыве от серьёзного по объёму кода? Чтобы обученный в итоге сразу видел применимость RAII к сетевому подключению или соединению с БД?

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

Прекрасно можно. Зачем там говорить про адреса?

И как вы будете объяснять результат выражения:

    std::vector<int> v = { 1, 2, 3};
    auto i = v[v.size()];

индекс это не адрес

Я не понял ответа. Давайте сузим вопрос: в этом коде есть какие-то проблемы или нет?

Чего непонятного в том, что я сказал? В этом коде ошибка выхода за границы массива, т.е. индекс больше, чем может быть. Зачем тут знать про адреса?

soomrack ★★★★★
()
Последнее исправление: soomrack (всего исправлений: 1)
Ответ на: комментарий от LamerOk

Как вы себе это представляете в объёме нескольких лаб в отрыве от серьёзного по объёму кода?

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

Например, можно дать задачу на работу с несколькими файлами посредством функций fopen/fclose (либо open/close под Linux/BSD, либо CreateFile/CloseHandle под Windows) и затем пальцем показать места в коде, где происходят утечки ресурсов. Потом рассказать про RAII и заставить переделать, возможно, усложнив задачу. Чтобы обучающиеся сами почувствовали, насколько стало проще.

Или задача на идиому pImpl. С ручным управлением памятью, без оного.

После пару-тройки таких задач навык находить паттерны для RAII у людей способных к программированию на C++, выработается. Если не выработается, то либо человек никогда не сможет нормально писать на C++, либо вообще не сможет программировать (нормально, в смысле, а не говнокодя).

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

eao197 ★★★★★
()
Последнее исправление: eao197 (всего исправлений: 1)
Ответ на: комментарий от soomrack

Чего непонятного в том, что я сказал? В этом коде ошибка выхода за границы массива, т.е. индекс больше, чем может быть. Зачем тут знать про адреса?

$  cat prog.cpp 
#include <iostream>
#include <vector>
 
int main(void)
{
    std::vector<int> v = { 1, 2, 3};
    auto i = v[v.size()];
    std::cout << i << std::endl;
    i++;
    v[v.size()] = i;
    return 0;
}
 
$ g++ prog.cpp -o prog
$ ./prog 
0

Но компилятор не видит никакой ошибки и всё работает.

Докажи, что тут ошибка.

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

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

А потом у них другие пары на других курсах, а потом следующий курс, а потом у них личная жизнь, выпуск, дискотека, ментовка, а потом, когда он приходит к тебе, все эти RAII и pimpl’ы находятся в той же области памяти, что и третья строка таблицы Менделеева и даты существования Нововавилонского царства.

Ровно этот этап обучения я называю нулём.

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

хочется - перехочется. канпилятор не обязан знать семантику не пойми каких функций и тем более шаблонов.

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

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

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

Зачем тут знать про адреса?

https://www.linux.org.ru/reactions?topic=18009687&comment=18016303

Ну вот ты и слился, потому что без адресов ты не сможешь объяснить даже простейшее UB в самом расхожем контейнере стандартной библиотеки.

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

Программа работает? Работает. Ты не прав. Докажи обратное.

Пока работает. потом сломается. У этого вектора внутри небось преаллокированный буфер на какой-то мизерный размер, и пока ты в него поместился, а как не поместишься - будет тебе щасте.

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

Ну вот ты и слился, потому что без адресов ты не сможешь объяснить даже простейшее UB в самом расхожем контейнере стандартной библиотеки.

Сменим пример на (не компилировался):

#include <map>

int main()
{
  std::map<int, int> m;
  m[0] = 1;
  return m.end()->second;
}

Этот UB тоже на указателях будете объяснять? При том, что итераторы контейнеров (особенно вроде std::map) вовсе не обязательно указатели вообще.

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

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

Не было бы такой привычки, было бы лучше. Отсюда и мой вывод о том, что учить Си перед C++ скорее вредно, чем полезно.

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

А путь изучения такой: Паскаль - Си - программа в 2000 строчек - C++

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

Этот UB тоже на указателях будете объяснять?

А каким образом объяснение одного UB избавит от необходимости объяснять другое UB? Или это какая-то новая игра "если UB с обращением по памяти, то нещитово"?

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

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

Совсем идиотом его считать тоже не стоит, просто желательно фильтровать сектантство от полезного преподавательского опыта.

Серьезно, программа в 2000 строчек кода (плюс-минус, это и 1000 строчек может быть) - это примерный порог сложности, когда начинает четко ощущаться полезность ООП и других вещей. До этого оно как бы в теории понятно, что хорошо, но не прочувствованно на себе.

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

А каким образом объяснение одного UB избавит от необходимости объяснять другое UB?

Потому что если объяснить людям концепцию итераторов и понятие past-the-end-iterator, то ваш пример с вектором не будет отличаться от моего примера с map. В обоих случаях проблема с применением невалидного итератора.

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

Серьезно, программа в 2000 строчек кода (плюс-минус, это и 1000 строчек может быть) - это примерный порог сложности, когда начинает четко ощущаться полезность ООП и других вещей

С этим я не спорю. Тут скорее вопрос в том, насколько это осуществимо в современном образовании :(

Ну и из вашей цепочки я бы Си выбросил за ненадобностью.

Мне бы больше понравилась связка Pascal - Asm - C++ - Java/C# - Python - JavaScript/TypeScript. Плюс, возможно, что-то из функциональщины. Вроде OCaml-а. А чистый Си не нужен. Имея представление о С++ до Си можно опуститься :)

Но не знаю насколько в современном мире рукопожат Pascal, может за одно его упоминание сразу забрасывают тухлыми помидорами. Ну и я про старый Pascal времен TurboPascal-какой-то там, с модулями, но еще без поддержки ООП.

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

Выход за границы массива. Индекс больше допустимого. Какое еще доказательство тебе надо?

Программа работает? Работает. Ты не прав. Докажи обратное.

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

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

Если человек так говорит, то значит у него много хаоса в голове.

Зря вы так. Учитывая что безглючных прог я ещё не видел (да да - даже хелло-вордов) умение оценить масштаб катастрофы и реальные последствия той или иной ошибки - очень и ОЧЕНЬ полезный практический навык.

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

Еще раз, – «Выход за границы массива. Индекс больше допустимого.» Это вполне себе доказательство некорректности программы. Это даже не программирование, это логика уровня 1-2 класса школы, ты пытаешься взять то, что не существует, в поезде 8 вагонов, а ты хочешь сесть в 9-й.

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

Это даже не программирование, это логика уровня 1-2 класса школы

Кардинально не согласен. Вот видите вы баг просочившийся в прод пол-года назад. И что, вы shot-gun’ить фикс будете? А там свои сопутствуюшие риски. А может и не так страшен чёрт как его малютка так как ввиду сторонних причин этот баг и не хитается вовсе. В общем - всё вовсе не так очевидно.

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

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

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

Обнаружить проблему

Дык, может это и не проблема вовсе, может этот UMR ни на что реально и не влияет. Я об этом. Есть очень большой gap между «стандарт говорит что это UB» и «а что реально произойдёт учитывая наш компилятор, runtime env и данные которые мы туда накидываем».

bugfixer ★★★★★
()

Пока еще никто не порекомендовал вроде

Лафоре Роберт. Объектно-ориентированное программирование в С++ / книги по языкам программирования.

Qui-Gon ★★★★★
()
Ответ на: комментарий от bugfixer

Выход за границы массива!

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

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

Хорошо, поставим вопрос по другому. Вот выучили мы некое подмножество С++ без адресной арифметики - а зачем? Какую задачу мы будем на нём решать, которая не решается на любом более другом языке без адресной арифметики?

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

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

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

таков дзен с++: не нравится - переопредели, мы тебе все дали.

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

Выход за границы массива! … Это реальная проблема этого кода.

И? По вашему это само по себе является проблемой вселенского масштаба достойной бросания на неё всех доступных ресурсов??

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

Хорошо, поставим вопрос по другому. Вот выучили мы некое подмножество С++ без адресной арифметики - а зачем?

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

Какую задачу мы будем на нём решать, которая не решается на любом более другом языке без адресной арифметики?

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

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

Например, собственный аллокатор на базе арены.

Да, для новичка в C++ – это оверкил, но для новичка и просто арифметика указателей – не меньший оверкил.

А вот вы можете привести примеры из практики, где в современном С++ нужна адресная арифметика? Может быть где-то в мире embedded? Потому что в более-менее прикладном C++ мне эта самая арифметика и не нужна особо, а там где есть что-то типа p++ или p-- (где p – это указатель), указатели оказываются частным случаем итераторов.

eao197 ★★★★★
()
Последнее исправление: eao197 (всего исправлений: 1)
Ответ на: комментарий от soomrack

Давайте так - прежде чем клоунами разбрасываться вы сначала заставите приведённый фрагмент сделать что-то хуже чем напечатать garbage (а скорее всего ноль на самом деле), даже тот вариант что пишет за конец вектора, а потом поговорим.

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

Давайте так - прежде чем клоунами разбрасываться вы сначала заставите приведённый фрагмент сделать что-то хуже чем напечатать garbage (а скорее всего ноль на самом деле), даже тот вариант что пишет за конец вектора, а потом поговорим.

Я клоуна поставил потому что ты бросаешься в крайности. И игнорируешь то, что я несколько раз повторил. А я прямо сказал, что это проблема кода, и сказал, что проблема и решение это разные вещи, про решение я ничего не говорил. Определять нужно ли решать эту проблему, как ее решать, зависит от ситуации, и даже видя весь код, в отрыве от бизнес процесса про это ничего сказать нельзя. Проблема найдена, обозначена, дальше решает тот кто должен решать, нужно ли ее закрывать, как закрывать и когда. Может этот код завтра уходит в архив и больше никогда не запускается…

Что этот баг может сделать в худшем случае? Да может просто привести к сегфолту из-за обращения к чужой памяти, если очень неповезет, а сегфолт может иметь кучу последствий.

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

soomrack ★★★★★
()
Последнее исправление: soomrack (всего исправлений: 1)
Ответ на: комментарий от LamerOk

на с++ без адресной арифметики быстро и эффективно решаются любые задачи, в которых не нужно знание о физическом расположении данных в памяти. А если такое знание необходимо, в бой вступают указатели. с криками - урааа!, сметая все на своем пути.

таков дзен с++.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от bugfixer

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

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

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

Да может просто привести к сегфолту из-за обращения к чужой памяти, если очень неповезет

Я бы сказал, что это еще очень повезет, если сразу будет сегфолт.

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

Хорошо, поставим вопрос по другому. Вот выучили мы некое подмножество С++ без адресной арифметики - а зачем?

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

А точно-точно нужна прям вся куча и именно на итераторах? Потому что 99% ежедневных потребностей покрываются встроенными в контейнер, а для оставшихся тривиально пишутся обобщённые методы:

using System;

namespace Algorithms.Search;

/// <summary>
///     Binary Searcher checks an array for element specified by checking
///     if element is greater or less than the half being checked.
///     time complexity: O(log(n)),
///     space complexity: O(1).
///     Note: Array must be sorted beforehand.
/// </summary>
/// <typeparam name="T">Type of element stored inside array. 2.</typeparam>
public class BinarySearcher<T> where T : IComparable<T>
{
    /// <summary>
    ///     Finds index of an array by using binary search.
    /// </summary>
    /// <param name="sortedData">Sorted array to search in.</param>
    /// <param name="item">Item to search for.</param>
    /// <returns>Index of item that equals to item searched for or -1 if none found.</returns>
    public int FindIndex(T[] sortedData, T item)
    {
        var leftIndex = 0;
        var rightIndex = sortedData.Length - 1;

        while (leftIndex <= rightIndex)
        {
            var middleIndex = leftIndex + (rightIndex - leftIndex) / 2;

            if (item.CompareTo(sortedData[middleIndex]) > 0)
            {
                leftIndex = middleIndex + 1;
                continue;
            }

            if (item.CompareTo(sortedData[middleIndex]) < 0)
            {
                rightIndex = middleIndex - 1;
                continue;
            }

            return middleIndex;
        }

        return -1;
    }
}

А вот вы можете привести примеры из практики, где в современном С++ нужна адресная арифметика?

А современность С++ погоды в целом не делает в этом вопросе вообще никакой.

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

LamerOk ★★★★★
()