Разобраться со слабыми сторонами C++?

Попробуем все разложить по полочкам:

70-e: Успех Си.
80-90-е: Программное обеспечение стало сложнее. Чтобы не сильно усложнять процесс разработки, понадобились новые абстракции:
  • наследование
  • приведение типов
  • перегрузка операторов
  • шаблоны
  • исключения
кроме того, не хотелось потерять в производительности.


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


Оказалось, что:
  • множественное наследование сложно использовать
  • перегрузка через virtual неудобна
  • исключения трудно реализовать в компиляторе, поэтому если и речь идет о многоплатформенности, то лучше о них забыть
  • шаблоны — не самый простой способ генеративного программирования
Всё это известно, но только в виде таких вот односложных утверждений, без исследований и живых примеров. Было бы здорово, если бы такие примеры нашлись. Сам я не занимаюсь профессионально разработкой на C++, поэтому обращаюсь за сим к хабрасообществу
  • Вопрос задан
  • 2818 просмотров
Пригласить эксперта
Ответы на вопрос 6
@Mercury13
Программист на «си с крестами» и не только
Что я могу сказать про проблемы C++?
1. Слишком слабая типизация. Например, int x = 0.0;
2. Система хедерных файлов крайне медленна, «предкомпилированные хедеры» и extern template — полумеры.
3. Запутано подключение чужого откомпилированного кода (DLL, к примеру). Мало написать хедер, надо ещё откомпилировать lib — в общем, интересного мало.
4. Библиотека STL крайне жирна. Хотя и libc тоже «хороша» — минимальная программа на Паскале занимала несколько килобайт, в зависимости от компилятора, на Си — приближается к сотне килобайт. Я не говорю про Linux/MSVC, где libc динамически подключаемая.
5. Строковый литерал на C++ — это та же нуль-терминированная строка. Когда эту строку приходится оборачивать в какой-нибудь std::string, уже при выполнении вычисляется её длина. Зачем? Почему бы не вкомпилировать её в exe'шник?
6. Нет ключевых слов override/reintroduce. При изменении сигнатуры виртуального метода приходится вспоминать, где он переопределялся.
7. Нет виртуальных конструкторов. «Фабрика» — полумера.
8. Коряво реализовано право доступа «читай кто угодно, пишу только я».
9. Явное определение методов как inline или не-inline в сочетании с шаблонами приводит к странным эффектам. Когда расшаблонивание приводит к сложному коду, inline вреден (сжирает кэш процессора), когда к простенькой операции с указателем — наоборот, нужен. В общем, это давно уже должно стать парафией оптимизатора.
10. В разного рода callback'ах замыкание приходится реализовывать собственными силами. Что-то типа: typedef void (*ProcDoSomething)(int aParam, void* aClosure). То же самое в Delphi: type ProcDoSomething = procedure(int aParam) of object;
11. Если вдруг случайно два разных модуля реализуют одно и то же, но один препроцессором, а второй — синтаксисом C++, будет ОЧЕНЬ много геморроя с поиском ошибки.
12. В обычном цикле for счётчик упоминается трижды. В общем, место очень ошибкоопасное. Для самых простых циклов у меня вообще есть макрос FOR_S (i, 0, n); суффикс S означает size_t.
13. Когда из-за рефакторинга «внутренней кухни» объекта меняется способ хранения ссылки, меняется и код, который этой ссылкой пользуется. Например: object.buddy.field, object->buddy.field, object.buddy().field — в зависимости от того, buddy реализовано как Buddy& buddy, Buddy* buddy или Buddy buddy().

Пока, засиделся. Мне бежать.
Ответ написан
tzlom
@tzlom
Слабая сторона С++ — ООП, потому что его нет
всё остальное это множество граблей для людей не понимающих как работает компьютер и/или С++, я сомневаюсь что это действительно проблемы языка
Ответ написан
Gorthauer87
@Gorthauer87
Программист
>Со временем стало ясно, что это не лучшее сочетание: небезопасная арифметика указателей и макросы хорошо подходили для низкоуровневого программирования, но на высоком уровне легко приводили к ошибкам.

Кто вас просит ими пользоваться? Я вот не пользуюсь (точнее оно нужно в 1% случаев).

>исключения трудно реализовать в компиляторе, поэтому если и речь идет о многоплатформенности, то лучше о них забыть

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

И на чем вы изволите писать большие проги, типа MSOffice? Он между прочим на плюсах полностью написан и поэтому работает очень быстро. На чем писать софт для мобилок, где проблемы с ресурсами стоят остро?
А многие проблемы с плюсами не столь серьезны, как любят многие говорить, а еще больше проблем возникает из за кривого проектирования фреймворков для плюсов. За примером ходить не надо — MFC, который и С++ фреймворком рука не поднимается назвать.
Ответ написан
wholeman
@wholeman
> множественное наследование сложно использовать
Простой пhимер: А имеет два подкласса: B и C, от них происходит D.
Если родительские классы имеют общего предка (A), то в потомке (D) будет несколько экземпляров этого предка. С каким будут работать унаследованные методы в общем случае неизвестно. При приведении указателя D* к A* также непонятно, на что он будет указывать.
Это можно исправить, объявив наследование от A, как virtual в B и C, но работать это будет значительно медленнее и инициализацию такого предка придётся делать в каждом потомке (B, C, D и далее по иерархии), а не только в непосредственных B и C. Это не только неудобно, но и не вписывается в ООП. Кроме того, если A,B и C объявлены в сторонней библиотеке, такая операция вообще невозможна.
> перегрузка через virtual неудобна
Не могу обосновать. Я не считаю её неудобной. Мне всегда нравилось.
> исключения трудно реализовать в компиляторе, поэтому если и речь идет о многоплатформенности, то лучше о них забыть
Забыть о них не получится, т.к. они используются в языке (оператор new, например, их использует). Другое дело, что мне, например, они неудобны, но я вообще склонен избегать выходов управления и середины функции, как, извините за грубость, оператора goto. В ряде случаев, впрочем, использую.
> шаблоны — не самый простой способ генеративного программирования
По моему, он достаточно прост. Может быть, даже слишком: с помощью шаблонов очень легко раздуть программу массой однотипного кода.
Ответ написан
Webchemist
@Webchemist
стали уже забывать былинные треды…
www.sql.ru/forum/actualthread.aspx?bid=16&tid=466654
Ответ написан
NanoDragon
@NanoDragon
Если кому-то что-то неудобно, то другому оно может быть очень даже удобно.
И наоборот.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

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