June 20th, 2021

cat with many words

c++

Читаю сейчас Beginning c++ from Novice to Professional, впечатления невероятные. C++ оказался именно таким языком, каким я его и представлял по обрывочным впечатлениям там и сям. До реально сложных вещей я еще не дошел, сейчас глава про references (как их там по-русски), но предыдущего введения уже хватило, чтобы составить впечатления. Интереса ради отмечаю в книге места, где автор предупреждает об очередном месте, где можно выстрелить себе в ногу.

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

Например:

  • Есть три разных способа инициализации переменной, часть с приведениями типа, часть нет
  • При передаче массива в качестве аргумента, теряется возможность узнать его размер, при этом объявить желаемую размерность можно. Если массив двумерный, то у второго измерения размер узнать или указать можно, будет работать.
  • Строки надо использовать std::string, они вроде бы конкатенируются через плюс, но не всегда, чтобы работало и литералами, нужно, чтобы один из аргументов был std::string, а то сломается.

Например,

"this" + "that" + some_str

по словам автором должно сломаться, т.к. литералы будут типа char*, и их сложение не определено. А вот так, например, работать будет

"this" + ("that" + some_str)

  • Строки можно складывать и с символами, но и тут есть засада. Если сделать так:

str += ',' + ' ';

то к строке прибавится не запятая с пробелом, а другой символ. Почему? Да потому что char - это численный тип, хоть и представляется литералами. В данном примере мы видим сумму двух char, так что компилятор их сложит как числа, а потом уже перегруженный оператор для строк добавит конечное значение char в строку. Каково? Меня взобдрило как следует

  • Ссылку на переменную надо очищать с помощью delete, ссылку на массив - с помощью delete[], но т.к. во многих местах массив даже передается как ссылка на элемент, точасто просто невозможно узнать, какой именно вызов делать, а перепутать нельзя.

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

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

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

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

Также понятно, почему ценят и любят опытных c++-программистов - уметь быстро и беспроблемно писать код на подобном языке действительно достойно всяческих похвал и уважения. Пока что кажется, что процесс похож на рубку леса ужасно эффективной бензопилой, придерживая ее за острие при работе. Вроде все нормально идешь, даже начинаешь улыбаться, а потом вдруг - хуяк! И нет руки.