LINUX.ORG.RU

10 причин почему программист на С++ может выбить много денег


24

10

Список в конце поста написан Лавсаном 2 года назад. (2011-03-23 19:56:00) (источник)
Надеюсь, автор не подаст жалобу в Роспатент за перепечатку :-)
Кстати, sudo cast lovesan.

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

Временное резюме: С++ всё еще актуален по историческим причинам. Еще есть мобилки (sudo cast mono), гиперкластеры для шиндовс 3.11 (sudo cast vromanov) и базы данных. Т.к. он актуален, но не предназначен ни для чего (см. выводы в конце списка) новых специалистов по нему должно быть мало. Маленькая конкуренция на огромной области применения — огромное лавэ $$$. Вот это и есть истинная причина использовать кресты — возможность срубить €€€.

Честно говоря, «хитрый план» мне уже очень надоел, поэтому пора открыть карты.

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

Вот этот список:

  1. Вырвиглазный синтаксис и контекстно-зависимая грамматика
    • медленная компиляция
    • частые «internal error» в компиляторах
    • код плохо читается и его сложно поддерживать
    • разбор кода различными инструментами, вроде IDE, и его генерация - сильно затруднены
  2. ручное управление памятью
    • неудобства при работе с динамической памятью
    • утечки памяти
    • висячие ссылки
    • сегфолты
    • стандартные средства, как то malloc/new, работают медленно
    • фрагментация кучи
    • велосипедные аллокаторы на каждом шагу
      • которые далеко не факт что эффективнее malloc/new

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

    • отладка затруднена
    • написание GC, по факту, невозможно, отчасти из-за (5), (7) и (8)
  3. Никакого ABI
  4. Нестандартизированный и непредсказумый name mangling
  5. Дублирование функционала Си
    • сами фичи из Си никуда не деваются при этом
      • отчасти из-за того, что по функционалу превосходят аналоги из C++

    • запутывает новичков
    • malloc - new/new[], free - delete/delete[]
    • препроцессор - шаблоны
    • указатели - ссылки
      • ссылка не может быть NULL, что способствует появлению висячих ссылок и сегфолтов

    • структуры - классы
    • stdio - iostream
  6. Стандартная библиотека убога
    • Отсутствует даже такой функционал, как вменяемая работа со строками и многомерные массивы
      • Юникод?

  7. Слабая типизация
    • способствует ошибкам
    • затрудняет отладку
    • const не дает абсолютно никаких гарантий
    • при этом система типов невероятно переусложенена
      • в основном из-за пунктов (2), (5) и (9)
      • медленная компиляция
      • частые внутренние ошибки в компиляторах

  8. объектая система убога
    • практически никакой интроспекции
      • отладка затруднена
    • передача объектов по значению
      • понятие идентичности объекта теряет смысл
      • добавляет сложностей в управлении памятью
      • добавляет сложностей при отладке
      • используется часто, по причине (2)
        • перерасход по памяти
        • медленная работа

    • множественное наследование неудобно в использовании
      • проблема ромба по дефолту не разрешается никак

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

    • деструктор можно вызывать до выхода из блока кода, или до delete
      • гарантированная утечка ресурсов/сегфлот
      • это не предотвратить никак, деструктор обязан быть public

    • одиночная диспетчеризация
      • виртуальные методы в конструкторах не работают
      • реализована убого
        • pure virtual function call
        • сложности в случае с множественным наследованием
        • деструкторы обязаны быть виртуальными
          • по дефолту - не виртуальные

        • никаких интерфейсов, только классы

    • порядок инициализации статических членов классов не определен
    • private, public и protected не дают никаких гарантий сокрытия данных
      • к инкапсуляции же не относятся совершенно никак

    • отсутствие «свойств»
      • вынуждает городить getter'ы и setter'ы
        • раздувание кода
        • размывание интерфейса класса

    • неявно генерирумые конструкторы, деструкторы и операторы присваивания
    • «friend» нарушают инкапсуляцию
  9. шаблоны
    • очень сильно замедляют компиляцию
    • раздувание кода
    • обфускация кода
    • результат раскрытия плохо предсказуем
    • сложности в отладке
      • километровые и плохо читаемые сообщения об ошибках при компиляции

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

    • позволяют генерировать некорректный код
  10. исключения
    • отсутствие finally/unwind-protect
      • заставляет городить классы ради одних деструкторов
        • раздувание кода
        • медленная компиляция
        • медленная работа

    • конфликтуют с другими возможностями языка
      • конструкторы/деструкторы
      • ручное управление памятью

    • работают медленно
    • малофункциональны (ср. CL condition system)

По причинам 3, 4, 5, 9 и 10 C++ совершенно неприменим для системного и низкоуровневого программирования. А по причинами 1, 2, 5, 6, 7, 8, и, опять же, 9 и 10 - и для прикладного.

У C++ нет области применения.

★★★★☆

Последнее исправление: stevejobs (всего исправлений: 1)

В целом — да, конечно, но это и так всем понятно.

По частностям:

1) частые «internal error» в компиляторах

В Visual-ных — ни разу не видел.

2) разбор кода различными инструментами, вроде IDE, и его генерация - сильно затруднены

Первое — да, но IDE плохо разбирают синтаксис чего угодно. И лисп не исключение.

Второе (про генерацию) — ну и хорошо.

3) велосипедные аллокаторы на каждом шагу

Нет. Только когда начинается борьба за проценты.

4) написание GC, по факту, невозможно

По факту GC есть, Boehm-овский. Вот написание ХОРОШЕГО GC — да, невозможно.

5) Нестандартизированный и непредсказумый name mangling

Да, но я ни разу не наблюдал, чтобы это представляло хоть какую-то проблему. Для FFI есть extern «C».

6) отчасти из-за того, что по функционалу превосходят аналоги из C++

По функционалу перфоратор превосходит электродрель. И что, собственно?

7) malloc - new/new[], free - delete/delete[]

Нет. Обычный new — это комбинация из malloc и placement new. Синтаксический сахар.

8) ссылка не может быть NULL, что способствует появлению висячих ссылок и сегфолтов

Во-первых, ещё как может. Во-вторых, при чём тут сегфолты?

9) структуры - классы

Вообще не дублирование, это одно и то же.

10) Стандартная библиотека убога

Смотря что считать стандартной библиотекой. Boost, зачастую, считается её частью.

11) Юникод?

Юникод.

12) объектая система убога... отладка затруднена

???

13) понятие идентичности объекта теряет смысл

Нет.

14) добавляет сложностей в управлении памятью

Нет.

15) используется часто

Нет.

16) множественное наследование неудобно в использовании

Наследование реализации неудобно всегда. Наследование интерфейса вполне удобно.

17) проблема ромба по дефолту не разрешается никак

Она решается при помощи virtual base.

18) поэтому ими стараются не пользоваться

Неверно. Конструкторами и деструкторами пользуются постоянно.

19) реализована убого

Реализована как раз весьма прилично.

20) никаких интерфейсов, только классы

Формально — да, фактически — полностью абстрактный класс от интерфейса не отличается ничем.

21) private, public и protected не дают никаких гарантий сокрытия данных

Э... ну, обойти protected — не проблема, паттерн «Паблик Морозов». Но private?

22) «friend» нарушают инкапсуляцию

Не согласуется со сказанным выше «к инкапсуляции же не относятся совершенно никак»

23) раздувание кода

Шаблоны как раз-таки сокращают код.

24) позволяют генерировать некорректный код

Естественно, но я пока не видел макросистемы, не позволяющей генерировать некорректный код.

Короче, до хорошей критической статьи ещё далеко.

Miguel ★★★★★
()

много букофф а вообще, поздно, слишком много кода на крестах

anonymous
()

Автор всё никак не может ответить на вопрос:

В чём смысл фразы «у C++ нет области применения»? Отсутствие области применения - плохо? А чем это плохо? Разве C++ плохо себя чувствует без области применения?

Автор упорно игнорирует этот вопрос!

kiverattes ★☆
()

Вырвиглазный синтаксис

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

медленная компиляция

Это плюс!

частые «internal error» в компиляторах

Не такие уж и частые

код плохо читается и его сложно поддерживать

«Неча на зеркало пенять»

ручное управление памятью

В грамотно написанном коде сводится к минимуму

неудобства при работе с динамической памятью

Решаются умными указателями

стандартные средства, как то malloc/new, работают медленно

А где они работают быстрее? В каких нибудь жабках с преаллоцированным пулом, или что там у них? При желании и на плюсах можно такое нарисовать, если оно действительно понадобится.

Дублирование функционала Си

Вот это вообще лол. С++ дублирует «функционал Си» в том плане, что тоже является компилируемым языком программирования не самого высокого уровня? Или речь идет о возможности использования Сишных библиотек? В общем кроме улыбки у меня этот пункт больше ничего не вызывает.

Стандартная библиотека убога

Постепенно расширяется, при наличии оставшейся части буста и прочих прикольных штук - не так уж критично.

объектая система убога

Зато она есть, работает и покрывает большинство случаев. Рецепты на случаи, когда её не хватает, тоже давно придуманы.

практически никакой интроспекции

А С++ - язык не того уровня, чтобы в нём была интроспекция по-умолчанию. Нулевая стоимость, все дела.

Ещё про наличие оператора присваивания забыли, хо-хо.

передача объектов по значению

На выбор программиста, может и не использоваться. Бывает даже полезна.

множественное наследование неудобно в использовании

Неудобно кому? Неудобно и всё. Замечательно :)

проблема ромба по дефолту не разрешается никак

А какая проблема у ромба? Вернее, какую из особенностей ромба считают за проблему?

исключения в конструкторах гарантированно влекут утечку памяти

Не тру: http://en.wikibooks.org/wiki/C++_Programming/Exception_Handling#Constructors_...

деструктор можно вызывать до выхода из блока кода, или до delete

С дури можно и болт сломать.

это не предотвратить никак, деструктор обязан быть public

Никому он не обязан.

вынуждает городить getter'ы и setter'ы

...которые приводят к нарушению инкапсуляции. Помню, помню тот былинный трэд о статье Голуба.

шаблоны

обфускация кода

Мда.

отсутствие finally/unwind-protect

RAII решает.

У C++ нет области применения.

Резюмируя: так докопаться можно до чего угодно, хоть до столба.

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

17) проблема ромба по дефолту не разрешается никак

Она решается при помощи virtual base.

Вот не знаю, это ли он имел ввиду. Ведь по идее ромб только и появляется при virtual base (без этого - не совсем ромб)

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

Плюс к трюку с дефайном

struct A {
private:
  int member;
};

//...

struct Amem { typedef int type; };
template class rob<Amem, &A::member>;

int main() {
  A a;
  a.*result<Amem>::ptr = 42; // have a nice debug
}

еще можно вручную сделать похожий класс, но чтобы соответствующее поле было public и сделать reinterpret_cast

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

сделать похожий класс, но чтобы соответствующее поле было public и сделать reinterpret_cast

private нужна криптозащита, не иначе, а то кто-нибудь ещё сделает похожий класс и reinterpret_cast случайно :)

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

«Зачем область применения, если и без неё неплохо живётся» — а вот это как раз про LISP/Haskell/Smalltalk/Brainfuck и прочую академически-яйцеголовую маргинальщину. Областей применения нет, практический софт писать невозможно, но ведь цель не в этом! Цель — обсуждать с собутыльниками коллегами зигохистоморфные препроморфизмы, анафорические лямбды, coKleisli-категории и смотреть на окружающих, как на говно.

Ну вообще многогранные знания ещё никому вроде не вредили, а подходы, усвоенные на новых языках/парадигмах, потом можно вполне успешно применять на том же С++. Сплошной профит!

yoghurt ★★★★★
()

Из всего этого больше всего достает отсутствие выразительных средств. Там где на C# или там Питоне можно написать не очень сложное LINQ выражение или там list comprehension в С++ нужно заводить какие-то дополнительные локальные типы и писать трехэтажные циклы. Новая редакция конечно внесла некоторые приятные дополнения типа лямбд и auto, но все равно по сравнению с более новыми языками кодинг на С++ вещь малоприятная.

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

хмм, еще недостаточно упоролся, чтобы так писать

anonymous
()
Ответ на: комментарий от nanoolinux

А ЛОР то на йаве!

шах и мат, крестопоклонники!

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

медленная компиляция

Это плюс!

Слова не мальчика, но мужа!

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

на сразу переделанное под вызов метода

template<typename Tag>
typename result<Tag>::type result<Tag>::ptr;

template<typename Tag, typename Tag::type p>
struct rob : result<Tag> {
	/* fill it ... */
	struct filler {
		filler() { result<Tag>::ptr = p; }
	};
	static filler filler_obj;
};

template<typename Tag, typename Tag::type p>
typename rob<Tag, p>::filler rob<Tag, p>::filler_obj;

struct A {
private:
	void f() {
		std::cout << "шах и мат, атеисты!" << std::endl;
	}
};

int main() {
  A a;
  (a.*result<Af>::ptr)();
}
stevejobs ★★★★☆
() автор топика
Ответ на: комментарий от dave

Да пофиг как внутри рантайма: код проектов с продакшена переписывали за ради нового сахара, а школьные косяки типа конкатенации строк плюсиками в цикле - успешно доживали до 4.0+

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

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

А ещё на Си можно руками в стэк залезть и адреса переписать... и что?

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 2)
Ответ на: комментарий от stevejobs

потому что невиртуальный деструктор в C++ не имеет практического смысла

RLY? А если ты не используешь в программе динамический полиморфизм, а только статический, и виртуальных методов у тебя нет - vtable не нужен, виртуальный деструктор тоже - программа говно? :)

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

>Но private?

Можно, но... «хлопотно это», да и практически не нужно :)

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

пока дело не доходит до каких-нибудь шаблонных классов в три этажа шаблонных аргументов.

На этот случай и придумали всякие well-grained design и прочие правила «не больше N-аргументов/членов», а если их больше - пожалуйте в рефакторингдополнительную декомпозицию.

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

Фокус с #define жеж.

А, да.

Но, вообще-то, используя string-based препроцессор можно обойти всё, что угодно.

Miguel ★★★★★
()
Ответ на: комментарий от stevejobs
test.cpp:2: error: expected nested-name-specifier before ‘result’
test.cpp:2: error: expected initializer before ‘<’ token
test.cpp:4: error: expected template-name before ‘<’ token
test.cpp:4: error: expected `{' before ‘<’ token
test.cpp:4: error: expected unqualified-id before ‘<’ token
test.cpp:12: error: template definition of non-template ‘typename rob<Tag, p>::filler rob<Tag, p>::filler_obj’
test.cpp: In function ‘int main()’:
test.cpp:23: error: ‘result’ was not declared in this scope
test.cpp:23: error: ‘Af’ was not declared in this scope
test.cpp:23: error: ‘::ptr’ has not been declared
Miguel ★★★★★
()
Ответ на: комментарий от invy

рефлекшн на жабе позволяет полностью перекроить класс вообще

1. SecurityManager
2. private static final, «константы», синтаксически их можно изменть, но из рантайма измененного значения не увидишь
3. кто сказал, что Java не протухла?

stevejobs ★★★★☆
() автор топика
Ответ на: комментарий от Boy_from_Jungle

стиви на столько толст, что пишет struct {}; вместо class {};

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

Использовал такое лишь для юнит-тестов через mockupы окружения классов и в велосипедных профайлерах. Хорошо, когда мемберы проекта в курсе :) Достаточно одного пионера с руками работающими вперед мозга, чтоб он эту штуку применил не по назначению.

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

Кастуй к войду* сразу и байтиками играй в буфере, чего уж там :)

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

Вы меня затроллить что ли решили? :3

src/yaoporot.cpp: http://pastebin.com/5mfyHBKq CMakeLists.txt: http://pastebin.com/1ihsjjGz

mkdir yauporot
cd yauporot
mkdir build
mkdir src

//положить файлы

cd build
cmake ..
make
./yauporot

Конпелятор

[olegchir@archi build]$ g++ --version
g++ (GCC) 4.8.1 20130725 (prerelease)
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

gcc из транка собирать не стал, уж извиняйте

на VisualStudio 2012 Update 3 тоже работает. Но это инфа не 100%, т.к. попросил знакомого — вчера удалил виндовс, остался один линукс, теперь мучаюсь

stevejobs ★★★★☆
() автор топика

imho

контекстно-зависимая грамматика

Хохо, ви так говорите, как будто это что-то плохое. И, да, КЗ или просто неоднозначная? А то ведь, знаете ли, и у бейсика грамматика (ВНИМАНИЕ!!!) КЗ.

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

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

.NET Reflector + открытые исходники F# (на гитхабе). Через первый я часто смотрю, как генерится мой код. Второе помогает понять, как устроены библиотеки, особенно Async.

dave ★★★★★
()

Делать вывода на основе баттхерта с псевдо-аргументацией как-то странно=)

anonymous
()

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

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

Спасибо

Однако код по ссылке сильно переусложнен, я упростил:

#include <iostream>

class Pew {
    void pew() {
        std::cout << "pew pew!" << std::endl;
    }
};

template<class Obj, void (Obj::*MemberFcn) ()>
class Caller {
    Obj &obj;

public:
    Caller (Obj &o) : obj (o) {
    }
    
    void operator() () {
        (obj.*MemberFcn) ();
    }
};

int main (int argc, char *argv[]) {
    Pew p;
    class Caller<Pew, &Pew::pew> call (p);
    call();
    return 0;
}

И вот тут я реально не понимаю, почему компилятор не ругается на

class Caller<Pew, &Pew::pew> call (p);

, при этом ругаясь на просто

Caller<Pew, &Pew::pew> call (p);

Матерь Божья!

g++ (Debian 4.8.1-9) 4.8.1 даже с -Wall ничего не говорит, а вот clang таки быкует:

/tmp $ clang++ pew.cc 
pew.cc:24:29: error: 'pew' is a private member of 'Pew'
    class Caller<Pew, &Pew::pew> call (p);
                            ^
pew.cc:4:10: note: implicitly declared private here
    void pew() {
         ^
1 error generated.
/tmp $ clang++ --version
Debian clang version 3.2-10 (tags/RELEASE_32/final) (based on LLVM 3.2)
Target: x86_64-pc-linux-gnu
Thread model: posix

Пополнил свою коллекцию, ещё раз спасибо :)

yoghurt ★★★★★
()

Причина только одна - специалистов мало и становится всё меньше, а легаси огромно. Те же, кого можно нанять - с десятилетним опытом соотв. рейтом.

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

Водителем фуры ты будешь столько зарабатывать, только если будешь возить вещества из Афганистана.

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

Napilnik ★★★★★
()

Вроде столько пунктов с разными номерами, но каждый пункт — 4.2.

quiet_readonly ★★★★
()
Ответ на: Спасибо от yoghurt

автор кода выцепил его как раз из clang'а. Вот его же упрощенная версия: https://gist.github.com/dabrahams/1528856

я порылся в ISO/IEC 14882:2011(E) и обнаружил следующее (14.7.2-12):

The usual access checking rules do not apply to names used to specify explicit instantiations. [ Note: In particular, the template arguments and names used in the function declarator (including parameter types, return types and exception specifications) may be private types or objects which would normally not be accessible and the template may be a member template or member function which would not normally be accessible. —end note ]

что походу является единственным разумным местом, куда можно пихать ссылки на private members

т.е.

template class rob<Af, &A::f>;

(в оригинале) или

template class stow_private<A_x,&A::x>;

является абсолютно валидным кодом по стандарту

с другой стороны (14.7.2-2)

The syntax for explicit instantiation is:
explicit-instantiation:
extern(optional) template declaration

т.е. твое упрощение
1) не эквивалентно исходному варианту
2) class Caller<Pew, &Pew::pew> call (p); — не подходит под форму explicit instantiation и работать не должно

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

Да, в студии тоже не работает. Тем не менее, gcc проглатывает.

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

Кстати, твой сайт, http://mm3.tiddlyspot.com/

там первая статья называется «hello peoples»

«посан» - это «person», а «превед посоны» - «hello people»

«a people» - это народ/нация/раса, и «hello peoples» - это превед нациям

это сайт националистического движения, или ты не осилил plural form? Может сначала ее, а потом уже кресты?

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

еще можно вручную сделать похожий класс, но чтобы соответствующее поле было public и сделать reinterpret_cast

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

m0rph ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.