Сравнительный анализ языков C# и C++

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

C++


Чему учит нас C++, что он нам говорит, какие парадигмы он воздвигает?

1. Что можно именовать типы, методы и переменные маленькими буквами

        std::list<std::string> L;
	L.push_back(50);
	L.push_front(-50);

Вы спросите, что же плохого в именовании элементов маленькими буквами?

А то, что, во-первых, C++ не различает контекст типа и контекст переменной и функции! И вы не можете никак взять и объявить поле, скажем, size size или point point! Поэтому, имеет смысл именовать функции и переменные маленькими буквами, но только если тип именуется в стиле C#, а именование типа маленькими буквами портит весь смысл такого действия! А смысл заключается в невозможности пересечения имени типа и его членов!

Ну, а во-вторых, именование как C#, выглядит солиднее, красивее.

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

2. Что можно сокращать так, что будет непонятно тому, кто использует ваш код, но зато «быстро печатается». Или сокращать, но все же понятно.

Вот лишь некоторые методы из стандартной библиотеки:

	std::string s; s.rfind(); s.c_str();
	std::fstream fs; fs.copyfmt(); fs.rdbuf();

Вы что-нибудь поняли? Что есть «rfind» и почему не «find», что это за r такая? Что такое «c_str»? Нужно посмотреть документацию, чтобы понять, что это превращение строки в «строку как в С» — массив-указатель на символы…

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

Вот например:

	#define foreach(x,c) for(auto x = c.begin(); x != c.end(); x++)
	#define min(x,y) ((x)<(y) ? (x) : (y))
	#define true false
	#define NULL 1

Обратите также внимание на то, что если параметры параметризованного макроса не обставлять скобками, то все вычисления с этим макросом легко испортятся! А дело в том, что параметры раскрываемого макроса воспринимаются как текст (!), и если не поставить скобки, то мы получим примерно такие варианты:

	a = b * min(x,y);  //==>  a = b*x < y ? x : y;
	a = min(5,3*k)*100; //==>  a = 5 < 3*k ? 5 : 3*k*100;

Согласитесь, это не то, что мы ожидали! И только познавшие эту хитрость макросов с параметрами на своем горьком опыте, долгих мытарствах и восклицаниях: «боже, что я не так сделал!» (или прочитавшие документацию к языку), пишут такие макросы правильно (и то, нужно быть очень внимательным!).

4. Что вы сами, сами! ‒ должны делать многие вещи, которые в C++ поленились сделать.

  • Не реализована нормальная работа стека. Я до сих пор не понимаю, как он работает! А где взять привычный нам хеш (словарь/карту)?
  • Выделение памяти (new/malloc) жутко тормозит! Вы сами, сами! должны делать управлятель памятью (ничего себе задача!), либо довольствоваться медленной работой
  • Не реализованы элементарные классы — класс «Массив», класс «Двумерный массив» и класс «Трехмерный массив».
  • Не созданы преобразования базовых типов: строковых и числовых (в том числе и логических), их надо делать самому (хотя, есть для этого возможности)

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

5. Что можно свободно баловаться с указателями и даже с константными указателями, надо лишь правильно преобразовать типы. Взгляните:

	void Func(const int* x) {
		((int*)x)[0] = 5;
	}

	int main() {
		int m = 500;
		Func(&m);
		std::cout << m <<'\n';
		return 0;
	}

И что же выведется у нас? 5! Константный параметр изменил свое значение! Так можно любые константные указатели менять, даже строковые или числовые (правда их изменение влечет ошибку обращения с памятью). Или вот:

	struct T {
	int M,N;
	T(int m, int n) {
		M = m; N = n;
	}
	};

	int main() {
		static const T t = T{5,3};
		((T*)(&t))[0] = T(-1,-5);
		std::cout << t.M <<" "<< t.N << "\n";
		return 0;
	}

Здесь изменяется статическая константа структурного типа! То есть, та, в чьей неизменяемости мы уверены на все 100%! «А вот нет!» — говорит на C++. И выводится именно изменённое значение константы.

6. Что можно вываливать содержимое перечислений в общий контекст! При чем, без включения стандарта C++11, мы не сможем сделать иначе.

Это ужасно! Взгляните на это:

	enum Month {
		JANUARY,FEBRUARY,
		MARCH,APRIL,MAY,
		JUNE,JULY,AUGUST,
		SEPTEMBER,NOVEMBER, DECEMBER
	};
	...
	Month m = JANUARY;
	...
	switch(x) {
	case JANUARY:
	case FEBRUARY:
	}

А если элементы пересекаются — присутствуют и в одном, и в другом перечислении (например, такие вполне могущие повторяться «NORMAL», «DEFAULT»)? Что тогда?.. C++ попускает вызов перечислений именно таким образом! Ленивые распущенные программисты C++ скажут: «А что, также быстрее пишется, также можно!», им невдомёк, что это распущенность, как невдомёк тем, кто ходит на современные российские комедии («А что, это прикольно, как в жызни!»)!

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

Хотя, конечно, вы можете быть порядочным и именовать элементы перечисления правильно (как сами перечисления (здесь, вообще, врятли будет пересечение имени типа и его элемента, потому может быть одинаковый стиль, что предпочтительнее, т.к. красивее)) и не вываливать элементы в контекст, даже запрещая это добавлением слова «class» после ”enum" (стандарт C++11). Но не факт и здесь, что остальные будут такими же порядочными и соблюдать правила!

Правильно делать так:

	enum class Month {
		January, February,
		March,April,May,
		June,July,August,
		September,October,November, December
	};

7. Что вовсе не являются ошибкой такие вещи как:

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

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

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

	void F(int& x, int& y);
	. . .
	F(x,y);
	void FF(int x, int y);
	FF(x,y);

Что мы видим? Мы видим, что ЕСЛИ УБРАТЬ ЗНАК ССЫЛКА, ТО ВЫЗОВ НЕ ИЗМЕНИТСЯ. Мы можем не знать, где ссылочный параметр, где параметр-значение! Лишь по смыслу функции или при активации подсказки в IDE мы можем это определить!

Подведём итоги


С++ нам говорит: у меня нет некоторых важных вещей, ты должен достать или написать их сам, а еще в нашем «демократическом» языке нет четких правил, живите как хотите!

С++ нас учит: пишите безобразно, развратно, используя макросы, двойные-тройные-четверные подчеркивания, сокращая имена до малопонятных, но быстропечатаемых, вываливая элементы перечислений в общий контекст, свободно обращаясь с указателями, даже изменяя содержимое констант, да и вообще на некоторые вещи мы не будем ругаться прерывая компиляцию, а лишь предупредим вас… Как бы говоря: вам никто не запрещает этого, все ограничения надуманы!

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

C#


Но взглянем на C#! Что же он нам говорит, какие парадигмы воздвигает, чему же учит?

1. Что слова в именах типов, функций, переменных начинаются с большой буквы и соединяются без разделителей (благодаря большим буквам слова видны). Это называется стилем C# (другое название ‒ стиль Pascal), представитель верблюжей группы стилей. Что первое слово имени каждой закрытой переменной начинается с маленькой, дабы не путать с открытыми ‒ стиль Java, второй представитель верблюжей группы стилей.

Контекст имен типов отличается от контекста имен функций, переменных и констант. Вы можете объявить поле Size Size или Point Point, и это скомпилируется и можно будет даже правильно использовать (даже сочетать имя поля и имя типа без использования this!)!

	struct Rect {
		Point Point;
		Size Size;
		// Функции...
		Rect(int x,int y,int w,int h) {
			Size = new Size(w,h);
			Point = new Point(x,y);
		}
		...
	}
	Rect r = new Rect(new Point(5,5), new Size(300,200));

2. Что никаких сокращений вне контекста функции! Своим стилем именования в стд. библиотеках C# показывает пример остальным ‒ как правильно именовать элементы кода, стиль этот красив и вдохновляет на подвиги. Никаких сокращений, верблюжий стиль с заглавной буквы, никаких подчеркиваний, вы их вообще не найдете! Каждое слово полно, либо является аббревиатурой (что, естественно допускается), также начинающейся с большой, а далее ‒ маленькими.

	XmlDocument document = new XmlDocument();
	Rectangle rect = new Rectangle(300,500,1000,500);

3. Возможности макросов урезаны до предела. Можно лишь объявлять и проверять, есть ли такой-то — такой-то макрос, а некоторые макросы объявляются через настройки компилятора. Всё!

	#define M
	...
	#if M
		double M = 53;
	#endif

Причём имя макроса и имя элемента (тип/функция/переменная/константа) параллельны, никак не связаны, можно одинаково называть и макрос и переменную (или другой элемент).

Вот какими безопасными могут быть макросы.

4. Что многие, многие вещи уже качественно сделаны в C#, а нормальный, солидный стиль делает их еще и самодокументирующимися, понятными, особенно после прочтения документации.

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

В основных сборках сделаны:
  • все базовые преобразования между строковым, числовым и логическим типами, преобразования находятся в классе Convert (System)
  • Вменяемые коллекции (System.Collections*)
  • Классы Xml (System.Xml*)
  • Сетевые классы (System.Net*)
  • Многопоточность (System.Threading*)
  • Классы по работе с процессами, диагностикой производительности, отладке (System.Diagnostics*)
  • и даже компилятор CodeDom для языков .NET! (System.CodeDom)
  • и многое другое

А есть еще много дополнительных, среди них можно выделить 2 основные, используемые в оконных приложениях: System.Windows.Forms* (по работе с GUI) и System.Drawing* (по работе с графикой)

Также, есть некоторые полезные встроенные вещи в языке, которых нет в C++:
  • встроенная конструкция foreach ‒ перечисление элементов коллекции
  • автоматический сборщик мусора, нам не нужно вручную освобождать память, а выделяется очень быстро, и не надо делать собственное управление памятью
  • свойства ‒ вместо того, чтобы писать методы доступа, можно писать свойства, которые объединяют в себе два метода доступа и не имеют параметров, все более лаконично, особенно сказывается на красоте интерфейса класса
  • всё наследует от объекта и может быть превращено в строку переопределяемым методом ToString()

	class Example {
		int X{get; set;}
		int Y{get; set;}
		object Object{
			get{return object;}
			set{object = value==null ? new object() : value;}
		}
	}
	...
	foreach(String arg in args) {
		Console.WriteLine( arg );
	}
	...


5. Что нельзя просто так взять и побаловаться указателями. Вам нужно для этого разрешить небезопасный код в настройках компиляции и обозначить класс как небезопасный, добавив «unsafe» в список его модификаторов. И только после этого можно будет с ними работать.
Кроме того, невозможно, невозможно! ‒ получить адрес константы или переменной только для чтения, что делает невозможным её изменение! ‒ и мы можем спокойно спать сладким сном, зная, что C# не даст константы и неизменяемые переменные в обиду всяким садистам, ой, то есть, изменять!

	unsafe class C {
		int* n;
		static void Main() {
			byte* n = (byte*)(5);
			n = (byte*)(n+3);
		}
	}

6. Что элементы перечислений остаются в контексте перечислений и не вываливаются в глобальный контекст. И они пишутся (по соглашению) в таком же стиле ‒ стиле C# ‒ как и имя содержащего их перечисления.

	enum DayOfWeek {
		Monday = 1, Tuesday, Wednesday,
		Thursday, Friday, Saturday,  Sunday
	}

В C# вообще ни одна функция/переменная/константа не может находиться вне класса.

7. Что многие ошибки, которые программист по невнимательности пропускает, на выявление которых можно определить алгоритм, C# обнаруживает и говорит, что это ошибка, прерывая компиляцию, если эта ошибка влияет на работу, а некоторые остаются как предупреждение, если не влияют. То есть, ошибки по невнимательности.

Среди ошибок:

  • возврат значения не во всех условных ветвях, либо отсутствие возврата значения
  • использование не определенной переменной
  • неявное преобразование большего по размеру в ОЗУ числа в меньшее по размеру в ОЗУ ‒ double==>float / long==>int==>short==>byte и т.п., а также знакового и беззнакового друг в друга: int<=>uint, short<=>ushort и т.п.; а также bool<=>число
  • неявное преобразование bool<=>число
  • перепрыгивание с одной метки case на другую, если данная не закрыта командой прерывания break, при том, что-то содержит или последняя.

Среди предупреждений:

  • не использованная переменная, функция, событие (приватная или локальная)
  • не достижимый код ‒ значит, перед ним стоит команда перехода (return/goto/break/continue)


Кроме того, параметры функции могут иметь один из двух модификаторов:
1. ref ‒ передача ссылки, в объявлении и передаче ссылке переменную нужно явно указать это слово впереди.
2. out ‒ то же самое, но переменная еще и может быть не объявлена

	void F(ref int x,ref int y){ ... }
	void O(out float j,out float k){ ... }
	...
	int x = 0, y = 0;
	F(ref x,ref y);
	int j,k;
	O(out j,out k);

И мы при вызове функции можем видеть, где мы передаем ссылку, а где ‒ значение. Это здорово, это хорошее правило языка.

Есть и другие полезные ограничения:

  • порядок с операторными функциями ‒ они все обязаны быть статичными
  • нельзя создать функцию/переменную/константу вне класса (ведь на любые действия или данные можно найти класс)

Подведём итоги


C# нам говорит: программируйте в удовольствие, у меня для этого всё есть: замечательные правила языка, правила именования, а также разные полезные библиотеки.

C# нас учит: используйте правильный стиль именования, забудьте про всякие подчеркивания, они излишни; забудьте про свободно гуляющие глобальные переменные и функции, у нас все находится в классах; не считайте ограничения надуманными, а считайте их правильными и упорядывающими ваше мышление, пишите по правилам и вы обретете нирвану!

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

Вывод


Если вы хотите стать приверженцем развращающей демократии C++, то туда вам и дорога, вы вправе сделать этот выбор. Но если вы готовы постичь нирвану в программировании, сохранить свои нервы, здоровье и здравомыслие, то ваш выбор ‒ C#.
Share post

Similar posts

Comments 77

    +30
    У вас проблема с названием статьи. Статью надо было назвать по другому. А именно «Почему С++ — отстой и чем его заменить».

    Не реализован класс массив? Нету нормальной мапы? А вы точно про С++?
      +2
      Ну вброс же.
      +21
      Добро пожаловать в клуб самоубийц… нашел, блин, с чем на C++ наезжать
        +1
        уважаю ваш ник :D
          +1
          Но ведь… автор из песочницы… его пригласило НЛО…
            +8
            Ну не повезло автору, бывает
            image
          +7
          Если Вы привыкли, что Вас должен язык вести за ручку по достаточно очевидным (в контексте написания чего-то сложнее лабораторных работ в универе) вещам и бить по рукам за то, что Вы не можете следовать элементарному Code Style — это, в первую очередь, Ваша проблема.
          И уж тем более не проблема языка в том, что Вы не смогли понять работу стека и не нашли, что «умный» массив в C++ называется vector, а также не приметили «словарь» и «карту» (хотя про set и map написано, наверное, в каждом учебнике по C++).
          C++ предполагает, что разработчик достаточно умный, чтобы не играться с преобразованием типов без полного понимания всех последствий, и чтобы в случае объявления аргумента-ссылки явно указывать «const» при необходимости. Ну, и к тому, что если метод принимал аргумент по значению, не стоит менять его на принимающий значения по ссылке без предварительного согласования всего вызывающего этот метод кода.
            +1
            По поводу const и способа их изменить через пень-колоду: const и прочие ограничения придуманы не для спасения от самого себя, а для спасения от законов Мёрфи.
            +38
            Мощнейший анализ двух ЯП
              +19
              Жалко, что хаб «Юмор» удалили.
                +4
                >> нельзя создать функцию/переменную/константу вне класса (ведь на любые действия или данные можно найти класс)
                Да, но всех настолько достало постоянно писать Math.sin(), что в C# 6.0 ввели статические импорты.
                  +2
                  Я так мечтал об этой фиче… Но как только она появилась понял, что она слишком непривычна.
                  Коллега сделал класс, где достаточно часто мелькали
                  if(IsNullOrWhiteSpace(arg))
                  
                  После прочтения пришло осознание, что
                  if(String.IsNullOrWhiteSpace(arg))
                  
                  гораздо читабельнее.
                  Хотя математикам проще, да.
                    0
                    вот, кстати, есть как минимум одно исследование, которое говорит, что_underscore_нотация_иди_snake_case_позволяет_легче_читать_код: ieeexplore.ieee.org/xpl/articleDetails.jsp?reload=true&tp=&arnumber=5521745

                    Ваш пример в snake_case:
                    if (is_null_or_white_space(arg))
                    


                    я же придерживаюсь внутреннего Style Guide по которому snake_case используется для свободных функций.
                      0
                      Google использует snake_case для функций, которые выполняются быстро, а CamelCase — для функций, выполняющихся долго и медленно. Тоже интересный подход.
                        0
                        Речь шла не про Style Guide, а про импорт статических классов.

                        Style guide-a в идеале придерживаются все участники проекта, независимо от личных предпочтений.

                        Импорт статики — дело (пока) сугубо добровольное, хотя R# и подталкивает пользоваться фичей. Проблема в том, что читать код с импортированными статическими классами непривычно.
                        Рассмотрим стандартные библиотеки. String.IsNull() и IsNull(), File.ReadAllBytes() и ReadAllBytes, Path.GetFileName() и GetFileName() — методы в любой из этих пар мне кажутся разными, читаемость падает, поэтому я не импортирую String, Path, File классы. В своих библиотеках я предпочитаю extension методы, и импорт статики ну нужен. В итоге фича применима только для ограниченного числа статических классов вроде Math, поскольку при его импорте читаемость кода не ухудшится.

                        При этом года через три этой фичей будут пользоваться гораздо активнее (в силу стандарта R#), и тогда привычки придется поменять.
                      –2
                      Ещё раздражает когда this. не пишут.
                      А за Point Point я пока не придумал как поквитаться.
                      +4
                      У С++ порог вхождения «немного» больше.
                        +6
                        Язык программирования — это инструмент. Он не должен вас ничему учить и ничего вам говорить, а наоборот: он предоставляет свой набор возможностей, и вы решаете, что из них вам нужно. Если язык позволяет больше, чем вам нужно (например, «Что можно именовать типы, методы и переменные маленькими буквами»), вы просто не используете эти возможности. Это плюс, а не минус.
                          –1
                          Тут самое главное, чтобы язык грамотно давал по рукам, когда хочется сделать чего-то «просто так», не подумав, но в то же время позволял извернуться в любой ситуации без излишнего brainfuck-ства. Так что в чем-то концепции шарпа правильные (особенно с приведением типов, впрочем, так во многих молодых и не очень (Java) языках).
                          +27
                          Нытье человека, освоившего C#, но которому лид дал задание что-то написать на C++
                            +8
                            Я подозреваю, что автор ещё не дорос до работы, уж очень школьный стиль опубликованного «анализа».
                              +4
                              Студент, или недавний студент. Недавно принят на работу. Месяц кодил тулзу для внутренних нужд с интерфейсом на DevExpress. Прилетел таск на основной код на C++, с которым знаком только в теории. Практика ужаснула.
                              Я сейчас описал реальный случай.
                                0
                                Повезло мне, что нам в университете преподавали плюсы. Не пишу на них уже лет пять-семь и не очень тянет, но уважение с тех пор осталось )
                            +4
                            Школьник освоил C# (ибо программист старой школы мельком знает про C/C++ и не обсирает почем зря).
                            Всю жизнь прожил по типу
                            C# нам говорит: программируйте в удовольствие, у меня для этого всё есть: замечательные правила языка, правила именования, а также разные полезные библиотеки.

                            И вдруг понадобилось подключить какую-то библиотеку или SDK на древнючем C++!!!… и тут мир перевернулся.
                            Не стоит так обсирать сей замечательнейший язык.
                            Вы даже не представляете какой нирваны можно можно достигнуть по пути C/C++.
                              +17
                              Образцово-показательный хабрасуицид. Ждём дальнейших откровений на тему «почему виндовс мастдай», «mongodb лучше, чем mysql» ну и что-то на тему почему тёплое более круглое, чем мягкое.
                                +2
                                По-моему этот пост уже переплюнул упомянутые вами темы )
                                +2
                                В чём вообще смысл таких сравнений? Шарп и плюсы — языки под разные задачи. На C# вы при всем желании не напишите, скажем, операционную систему. Инструмент правильный подобрать настоящий джедай должен.
                                  0
                                  Справедливости ради, нужно отметить Cosmos, SharpOS и Singularity :-) Но…
                                  +2
                                  «именование как C#», есть camel case, есть snake case, а про такое именование не слышал.
                                    0
                                      0
                                      Точнее, это Visual Studio Style, если не ошибаюсь. У C# разные имплементации, и стиль тоже отличается (от рекомендованного в Mono, допустим).
                                      +2
                                      Несколько более обширное сравнение C++, C# (а также Objective C и Java): hyperpolyglot.org/cpp (с примерами кода и таблицей)
                                        –11
                                        Сравнение конечно не самое лучшее и многое о чем автор позабыл (и дремучие ошибки компиляции темплейтов и сама скорость компиляции и вообще инструменты отладки кода (увы, это относится и к языку, т.к. сделать для C++ аналогичные удобные инструменты малореально), и сахарные возможности C# вроде оператора?.. и многое-многое другое) Однако автор мыслит абсолютно прагматично. И я бы сделала такой вывод: при создании нового проекта где нет жестких требований вроде переносимости под какие-то редкие платформы и/или полное отсутствие рантайма и сборщика памяти или необходимости тесного взаймодействия с другими С++ библиотеками, то писать такое приложение на С++ — чистое безумие.
                                          +3
                                          Вот это по нашему. Ни одного слова про CLI и рантайм. Вы уж определитесь что сравниваете.
                                            –3
                                            Раз в пару месяцев появляется (пролазит в обсуждаемое) такая статейка. И главное, почти все время C++ и C#, нет бы Javascript и LISP или Visual Basic и Пролог. C++ народ защищает, С# не очень, видимо сказывается меньшая популярность. И конечно, любая такая статья неполна без пары мудрых комментариев что для каждой задачи свой инструмент.
                                              –1
                                              Да! Как эти коментарии достали!
                                                +3
                                                Просто идиотизм статьи зашкаливает. Есть С# у которго свой рантайм и С++ который компилируется в «нормальный» бинарник. Есть С++ CLI который вроде как С++11, но там таблица с расхождениями от стандарта на 10 листов А4, есть managed C++ — кадавр для рантайма .NET, есть еще куча тонкостей. Дак либо сравнивайте сам синтаксис языков и не пихайте в сравнение стандартные бибилитеки. Либо сравнивайте CLR и С++ — «какая удобная стандартная библиотека». Есть еще другие варианты, а тут бред на уровне «крокодил больше круглый чем зеленый, щас я вам это докажу!». Нет бы тиснуть статью что например в C# override был уже давно, а в С++11 появился впервые и о чудо, это поддерживается реализацией MSVS начиная с 2010, с минимальным отходом от стандарта.
                                                  +1
                                                  >Есть С++ CLI который вроде как С++11, но там таблица с расхождениями от стандарта на 10 листов А4
                                                  C++/CLI — это не C++11, это набор расширений для оного, чтобы из нативного C++ кода можно было взаимодействовать с управляемым, а из управляемого — с нативным. Другими словами, C++/CLI — это надмножество C++.

                                                  >есть managed C++
                                                  Это старая версия C++/CLI.
                                                    0
                                                    Есть еще C++-cx, который Nanako имел в виду, видимо
                                                      0
                                                      Именно. Просто я на C# в основном пишу, а в вариантах С++ путаюсь постоянно. С и С++ я по большей части через вообще через nmake собираю. Больших проектов на них у меня нет, в итоге мне понятнее получается.
                                                0
                                                вы не можете никак взять и объявить поле, скажем, size size или point point
                                                Можем (небольшое пояснение), но не стоит так делать. Да, сработает не всегда, но это не «никак».
                                                  +2
                                                  С++ позволительно критиковать только двумя способами:

                                                  1) C++ N лучше в указанных вопросах, чем бардак C++ N-1. И это интересная и полезная критика.

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

                                                  В остальных случаях это holy-war и flame, иногда с утерей кармы.
                                                    +9
                                                    Пример с константным указателем сделал мой день в стиле «я докажу вам что ваш язык отстой, потому что я не умею им пользоваться».
                                                      +3
                                                      Видно, что у него и с C# то плохо, особенно повеселил пример с «макросами».
                                                        +1
                                                        Тем более, что в шарпе в последних версиях разрешили не писать ref и out при вызове. На мой взгляд, зря, это как раз полезно было понимать, что аргумент может/должен поменятся.
                                                          0
                                                          У меня до сих пор требует, что, тем не менее, не огорчает :)
                                                          –6
                                                          #include <iostream>
                                                          #define profit int main(int argc, char *argv[]) {std::cout << "MACROS MASTER!!!";  return 0; }
                                                          profit
                                                          

                                                          Шах и мат, Автор!
                                                        +3
                                                        Вы о соглашениях именования хоть немного слышали?
                                                        Они не ограничиваются только Pascal style — https://msdn.microsoft.com/ru-ru/library/ms229043.aspx
                                                        Ну а все остальные «доводы» просто смешны.
                                                        К вашему сведению уже давно есть C++11, C++14 с строгими перечислениями (enum class) и умными указателями.
                                                        Не нужно смешивать C++ и C# — первый это продолжатель Си, второй был создан как помесь Delphi с синтаксисом Java что бы и вы могли «программировать».
                                                          +2
                                                          Так толсто, что даже тонко.
                                                            +2
                                                            Выделение памяти (new/malloc) жутко тормозит! Вы сами, сами! должны делать управлятель памятью (ничего себе задача!), либо довольствоваться медленной работой


                                                            Так в C# выделение тоже тормозит, просто это делается, например, при запуске.
                                                            Открою секрет: в C# выделение памяти происходит точно так же через malloc (желающие могут погуглить точное название функции из WinAPI), как и в любой операционной системе.
                                                              0
                                                              Ну и это претензия не к собственно языку, а к инфраструктуре.

                                                              На C++/CLI всё так же, как в C#.
                                                                0
                                                                Нет, в C# быстрее.
                                                                Выделение объекта с полем 1 байт в 3.736 быстрее, а выделение массива 100 байт в 3.004 раз быстрее.
                                                                  +2
                                                                  Покажите методику расчёта. Логически подумайте, в C++ это те же самые вызовы WinAPI.

                                                                  Уверен, ошибка либо в методах измерений, либо в C# вы «выделяете» память со стека, а в C++ — с кучи.
                                                                    –2
                                                                    using System;
                                                                    using System.Collections.Generic;
                                                                    using System.Linq;
                                                                    using System.Text;
                                                                    using System.Threading.Tasks;
                                                                    
                                                                    namespace PerfomanceTests {
                                                                    	public class PerfomanceTest {
                                                                    		public delegate void PerfomanceTestAction();
                                                                    		String title;
                                                                    		PerfomanceTestAction action;
                                                                    		int count;
                                                                    		
                                                                    		public PerfomanceTest(String title, PerfomanceTestAction action, int count) {
                                                                    			this.action = action;
                                                                    			this.count = count;
                                                                    			this.title = title;
                                                                    		}
                                                                    		
                                                                    		public long MeasurePerfomance() {
                                                                    			Stopwatch watch = new Stopwatch();
                                                                    			watch.Start();
                                                                    			int i = 0;
                                                                    			LABEL:
                                                                    				action();
                                                                    				i++;
                                                                    			if(i < count) goto LABEL;
                                                                    			watch.Stop();
                                                                    			return watch.ElapsedMilliseconds;
                                                                    		}		
                                                                    
                                                                    		public void Run() {
                                                                    			WriteLine("Perfomance of "+title +":  " + MeasurePerfomance()+" ms");
                                                                    		}
                                                                    	}
                                                                    	
                                                                    	class Program {
                                                                    		class T {
                                                                    			byte c;
                                                                    		}
                                                                    
                                                                    		static void Main(string[] args) {
                                                                    			new PerfomanceTest("MAlloc", PFMAlloc, 10000000).Run();
                                                                    			Console.WriteLine("End!");
                                                                    			Console.ReadLine();
                                                                    		}
                                                                    				
                                                                    		static byte[] x;
                                                                    		static void PFMAlloc() {
                                                                    			x = new byte[100];
                                                                    			//new T();
                                                                    		}
                                                                    	}
                                                                    }
                                                                    


                                                                    #include <iostream>
                                                                    #include <string.h>
                                                                    #include <random>
                                                                    #include <time.h>
                                                                    #include <string>
                                                                    #include <sstream>
                                                                    
                                                                    void TestPF(void func(), size_t count) {
                                                                      size_t i = 0;
                                                                      clock();
                                                                      int time0 = clock();
                                                                        while(i < count) {
                                                                          func();
                                                                          i++;
                                                                        };
                                                                      int time = clock()-time0;
                                                                      printf( "Time=" );
                                                                      printf( "%i\n", time );
                                                                    };
                                                                    
                                                                    void PFMAlloc() {
                                                                      malloc(100);
                                                                    }
                                                                    
                                                                    int main() {
                                                                      TestPF(PFMAlloc, 10000000);
                                                                      return 0;
                                                                    }
                                                                    


                                                                    и не надо тут разводить, типа шарп медленнее в этом вопросе
                                                                      +2
                                                                      1) Мерьте не со статическим значением, известным в compile-time. Здесь просто C# схлопнул выделение памяти ужемна этапе компиляции, там фактического выделения и не было.
                                                                      2) Мерьте с помощью std::high_resolution_clock. Здесь вы измерили сишные (не c++) функции clock() и т.п.
                                                                      3) Мерьте одинаковое: в C++ тоже делайте new int[size].

                                                                      Здесь, очевидно, ошибка в измерениях. Потому что оптимизирующий режим компилятора должен был выкинуть неиспользуемые вызовы malloc.
                                                                      И больше не пишите такой жуткий old C-style код.

                                                                      Я не говорил, что C# тормознее, я говорил, что в скорости выделения памяти все языки должны быть одинаковы, потому что по сути это один системный вызов.
                                                                        –1
                                                                        Ничего C# не схлопнул на этапе компиляции, не надо наговаривать!
                                                                        clock нормально вычисляет.
                                                                        И там, и там одинаковое количество байт одинаково выделяется!

                                                                        Количество системных вызовов можно уменьшить, просто выделив запас, и в дальнейшем выдавая указатель на уже ранее выделенный, свободный участок памяти (в этом запасе)!
                                                                          +2
                                                                          Как-то вы неконструктивно с этими эмоциональными заявлениями.

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

                                                                          Если язык с рантайм-оверхедом побеждает язык без оверхеда, то там происходит какая-то хитрость, например, количество выделяемых байт известно на этапе компиляции, и память выделяется уже при старте приложения ОДИН раз на весь необходимый объём (что и происходит в C#).

                                                                          Судя по вашему тону и качеству кода даже на C# (про C++ не говорю вообще, то что вы написали — близко не C++), вы ещё студент, и можете не знать, как работает .NET.

                                                                          То же самое можете сделать и в C++: выделите сразу всю память, а потом делайте placement new.
                                                                            –1
                                                                            То же самое можете сделать и в C++: выделите сразу всю память, а потом делайте placement new.

                                                                            нет, там не вся память выделяется. а кусками.
                                                                            и это не так просто как ты думаешь. это надо хорошо подумать как сделать.
                                                                              +1
                                                                              Вы на самом деле не понимаете, как работает .NET, а ещё пытаетесь защищать. Поизучали бы литературу. Я уж на что не любитель .NET, и то мне очевидно, что раз компилятор знает на этапе компиляции, сколько ему памяти потребуется, то он выделит всю память заранее. Как и в любом языке, где о программистах думают, что они неспособны следить за ресурсами, и поэтому вводят GC.
                                                                                –1
                                                                                Чума на оба ваших дома!

                                                                                Один написал тест, в которcом сравнивает new c malloc и считает, что сравнил С# с C++, другому очевидно, что компилятор C# выделит всю память заранее. Чего ж C++ то заранее память не выделит, а?

                                                                                В общем. Я не специалист по С++. И по С#. Тем не менее хочу поинтересоваться каким компилятором собирался код для C++? Под какую платформу? Какие флаги оптимизации были использованы?

                                                                                Под какой виртуальной машиной работает C#? На какой платформе?

                                                                                И почему для выделения памяти в C# используется new, а в С++ malloc?
                                                                                  0
                                                                                  > Чего ж C++ то заранее память не выделит, а?

                                                                                  Потому что в C++ такого нет. Компилятор делает такой код, который делает именно то, что ему говорят, и только в нужное время (когда ему говорят).

                                                                                  > И почему для выделения памяти в C# используется new, а в С++ malloc?

                                                                                  По сути это одно и то же, new в любой реализации под основные платформы и компиляторы вызывает malloc (или его аналог).

                                                                                  Но согласен, что нужно измерять одинаковое, о чём я и сказал.
                                                                                    +1
                                                                                    В С++ такое как раз сплошь и рядом. Если что-то можно посчитать во время компиляции, многие компиляторы это охотно делают. Если компилятор видит цикл, в котором считается значение переменной, которая после этого не используется — он уберёт этот цикл из программы. И так далее.

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

                                                                                    А вот виртуальные машины такими трюками известны гораздо меньше. Я даже и не припомню.
                                                                                      0
                                                                                      В С++ такое как раз сплошь и рядом. Если что-то можно посчитать во время компиляции, многие компиляторы это охотно делают. Если компилятор видит цикл, в котором считается значение переменной, которая после этого не используется — он уберёт этот цикл из программы. И так далее.


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

                                                                                      Про количество выделямой памяти: компилятор (AOT/JIT) языков с гарбадж-коллектором заранее выделяет нужное (или, если оно неизвестно точно, просто заранее огромным куском) количество памяти с кучи (методами операционной системы) и далее распределяет её эффективнее. Это как раз удобно всяким .NET- и Java-программистам, не желающим следить за памятью.
                                                                                        0
                                                                                        >если ты сказал выделить память, и даже если ты потом её не используешь совсем, то компилятору запрещается это вырезать. Если ты сказал открыть сокет, но потом туда ни пишешь, ни читаешь, то компилятору запрещается оптимизировать код так, чтобы сокет не открывался.

                                                                                        На самом деле компилятору ни то, ни то не запрещается. Точнее даже сказать, что как правило он ни о том, ни о другом даже понятия не имеет, потому что это все — внешние вызовы. И вот уже внешние вызовы компилятор убирать не имеет права, ибо он не знает, что за ними скрывается.
                                                                                          +1
                                                                                          Точнее даже сказать, что как правило он ни о том, ни о другом даже понятия не имеет, потому что это все — внешние вызовы.


                                                                                          Я тут погуглил и понял, что был не совсем прав. Clang/LLVM (начиная с версии 3.2) и GCC (начиная с версии 5.1) умеют в malloc/calloc/alloca-оптимизации. А вот хвалёный Intel не умеет.

                                                                                          Например, код

                                                                                          #include <string.h>
                                                                                          #include <stdlib.h>
                                                                                          
                                                                                          int main(void)
                                                                                          {
                                                                                            size_t n = 1000;
                                                                                            int *a = (int*)calloc(n,sizeof(int));
                                                                                            int *b = (int*)malloc(n*sizeof(int));
                                                                                            if (a && b)
                                                                                              memcpy(b,a,sizeof(int)*n);
                                                                                            return 0;
                                                                                          }
                                                                                          


                                                                                          будет соптимизирован (начиная с -O1) в пустышку:

                                                                                              int main(void)
                                                                                              {
                                                                                                  return 0;
                                                                                              }
                                                                                          


                                                                                          Асм-код:

                                                                                          main:                                   # @main
                                                                                                  xorl    %eax, %eax
                                                                                                  retq
                                                                                          


                                                                                          Поглядеть можно здесь: goo.gl/ILSQvi

                                                                                          (Я сейчас офигел.)
                                                                                            0
                                                                                            И, получается, топик-стартер вообще был нечестен.

                                                                                            Т.к. функция

                                                                                            void PFMAlloc() {
                                                                                              malloc(100);
                                                                                            }
                                                                                            


                                                                                            компилируется в:

                                                                                            PFMAlloc():                           # @PFMAlloc()
                                                                                                    retq
                                                                                            


                                                                                            Я там внизу оставлю ещё коммент.
                                                                                            0
                                                                                            Это как раз удобно всяким .NET- и Java-программистам, не желающим следить за памятью.

                                                                                            Вот это сильно. С потоком, например, в 4 гигабита внутри программы за памятью надо следить в любом случае. И для меня на .NET это просто быстрее реализовать.
                                                                                              0
                                                                                              А что за программа на .NET с потоком 4 гигабита внутри программы?
                                                                                                0
                                                                                                Моя программа, десктопная. У серверного .NET для высоких нагрузок другие режимы сборки мусора доступны. Да и на Java куча highload софта написана.
                                                                            +1
                                                                            Кто же new c malloc сравнивает. Или используйте unsafe или добавляйте critical section. malloc синхронизирует каждый вызов к куче, new в C# этого не нужно.
                                                                              0
                                                                              А C++ new можно сравнивать с C# new?
                                                                                +2
                                                                                Да тоже нет, new это емнип malloc + конструктор. У C# память выделяется огромными блоками, поэтому вызовов malloc вообще минимальное количество, доступ к куче не локается и куча тредов может параллельно объекты создавать, т.к. память системой давно уже выделена. Для C++ есть же готовые решения для пакетного malloc, вот их и надо сравнивать с C#. Хотя сам идея сравнивать «высокоуровневый ассемблер» с «рантайм песочницей» — бред.
                                                                              +1
                                                                              Скомпилируйте C++ с оптимизацией и вы немного удивитесь:

                                                                              vladon@vldn-dev:~/projects/temp$ clang++ -O3 -std=c++11 test.cpp -o test
                                                                              vladon@vldn-dev:~/projects/temp$ ./test
                                                                              Time=0
                                                                              


                                                                              А я-то не стал за вами проверять. Эх я, пепел на мою голову.
                                                                        +4
                                                                        Кстати, неплохой вариант попасть на хабр. Пишем подобную статью, получаем приглашение от НЛО, обнуляем карму, ждем пару недель. На выходе нулёвый акк. Profit же.
                                                                          0
                                                                          Есть менее трудозатратные способы. Например, написать о чем-нибудь интересном…
                                                                            0
                                                                            Интересно, а на гиктайм, и мегамозг как попадают.
                                                                            0
                                                                            Если вы хотите стать приверженцем развращающей демократии C++, то туда вам и дорога, вы вправе сделать этот выбор. Но если вы готовы постичь нирвану в программировании, сохранить свои нервы, здоровье и здравомыслие, то ваш выбор ‒ C#.

                                                                            А если, я хочу скорость? Не раскрыт вопрос того как добиться высокой скорости на C#, или про это будет отдельная статья?

                                                                            Only users with full accounts can post comments. Log in, please.