Пять популярных мифов про C++, часть 1

http://www.stroustrup.com/Myths-final.pdf
  • Перевод

1. Введение


В этой статье я попытаюсь исследовать и развенчать пять популярных мифов про C++:

1. Чтобы понять С++, сначала нужно выучить С
2. С++ — это объектно-ориентированный язык программирования
3. В надёжных программах необходима сборка мусора
4. Для достижения эффективности необходимо писать низкоуровневый код
5. С++ подходит только для больших и сложных программ

Если вы или ваши коллеги верите в эти мифы – эта статья для вас. Некоторые мифы правдивы для кого-то, для какой-то задачи в какой-то момент времени. Тем не менее, сегодняшний C++, использующий компиляторы ISO C++ 2011, делает эти утверждения мифами.

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

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

2. Миф 1: Чтобы понять С++, сначала нужно выучить С


Нет. В С++ проще изучать основы программирования, чем в С. С – это почти подмножество С++, но не лучшее из них, потому что у С нет типобезопасности и удобных библиотек, которые есть у С++ и которые облегчают выполнение простых задач. Рассмотрим простой пример создания емейл-адреса:

string compose(const string& name, const string& domain) 
{ 
  return name+'@'+domain; 
}


Используется он так:

string addr = compose("gre","research.att.com"); 


Естественно, в реальной программе не все аргументы будут строками.

В С-версии необходимо напрямую работать с символами и памятью:

char* compose(const char* name, const char* domain) 
{ 
  char* res = malloc(strlen(name)+strlen(domain)+2); // место для строк,  '@', и 0 
  char* p = strcpy(res,name); 
  p += strlen(name); 
  *p = '@'; 
  strcpy(p+1,domain); 
  return res; 
} 


Используется он так:

char* addr = compose("gre","research.att.com"); 
// … 
free(addr); // по окончанию освободить память 


Какой вариант легче преподавать? Какой легче использовать? Не напутал ли я чего в С-версии? Точно? Почему?

И, наконец, какая из версий compose() более эффективная? С++ — потому что ей не надо подсчитывать символы в аргументах и она не использует динамическую память для коротких строк.

2.1 Изучение С++

Это не какой-нибудь странный экзотический пример. По-моему, он типичен. Так почему множество преподавателей проповедуют подход «Сначала С»? Потому, что:

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

Но С – не самое простое или полезное подмножество С++. Зная достаточно С++, вам будет легко выучить С. Изучая С перед С++ вы столкнётесь со множеством ошибок, которых легко избежать в С++, и вы будете тратить время на изучение того, как их избежать. Для правильного подхода к изучению С++ посмотрите мою книгу Programming: Principles and Practice Using C++. В конце есть даже глава про то, как использовать С. Она с успехом применялась в обучении множества студентов. Для упрощения изучения её второе издание использует С++11 и С++14.

Благодаря С++11, С++ стал более дружественным для новичков. К примеру, вот вектор из стандартной библиотеки, инициализированный последовательностью элементов:

vector<int> v = {1,2,3,5,8,13}; 


В C++98 мы могли инициализировать списками только массивы. В С++11 мы можем задать конструктор, принимающий список {} для любого типа. Мы можем пройти по вектору циклом:

for (int x : v) test(x); 


test() будет вызвана для каждого элемента v.

Цикл for может проходить по любой последовательности, поэтому мы могли бы просто написать:

for (int x : {1,2,3,5,8,13}) test(x); 


В С++11 старались сделать простые вещи простыми. Естественно, без ущерба быстродействию.

3. Миф 2: С++ — это объектно-ориентированный язык программирования


Нет. С++ поддерживает ООП и другие стили, но он не ограничен специально. Он поддерживает синтез программных стилей, включая ООП и обобщённое программирование. Чаще, лучшим решением задачи будет использование нескольких стилей. Лучшим – значит, более коротким, самым понятным, эффективным, обслуживаемым, и т.п.

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

Пример:

void rotate_and_draw(vector<Shape*>& vs, int r) 
{ 
  for_each(vs.begin(),vs.end(), [](Shape* p) { p->rotate(r); }); // повернуть все элементы vs 
  for (Shape* p : vs) p->draw(); // нарисовать все элементы vs 
} 


Это ООП? Конечно – тут есть иерархия классов и виртуальные функции. Это обобщённое программирование? Конечно, тут есть параметризованный контейнер (вектор) и обычная функция.

for_each. Это функциональное программирование? Что-то вроде того. Используется лямбда (конструкция []). И что же это за стиль? Это современный стиль С++11.

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

3.1 Обобщённое программирование.

Хотите более обобщённого кода? В конце концов, он работает только с векторами указателей на Shapes. Как насчёт списков и встроенных массивов? Что насчёт «умных указателей», типа shared_ptr и unique_ptr? А объекты, которые называются не Shape, но которые можно draw() и rotate()? Внемлите:

template<typename Iter> 
void rotate_and_draw(Iter first, Iter last, int r) 
{ 
  for_each(first,last,[](auto p) { p->rotate(r); }); // повернуть все элементы [first:last) 
  for (auto p = first; p!=last; ++p) p->draw(); // нарисовать все элементы [first:last) 
}


Это работает с любой последовательностью. Это стиль алгоритмов стандартных библиотек. Я использовал auto, чтобы не называть типы интерфейса объектов. Это возможность С++11, означающая «использовать тип выражения, который был использован при инициализации», поэтому для p тип будет тот же, что и у first.

Ещё пример:

void user(list<unique_ptr<Shape>>& lus, Container<Blob>& vb) 
{ 
  rotate_and_draw(lus.begin(),lus.end()); 
  rotate_and_draw(begin(vb),end(vb)); 
}


Здесь Blob – некий графический тип, имеющий операции draw() и rotate(), а Container – тип некоего контейнера. У списка из стандартной библиотеки (std::list) есть методы begin() и end(), которые помогают проходить по последовательности. Это красивое классическое ООП. Но что, если Container не поддерживает стандартную запись итераций по полуоткрытым последовательностям, [b:e)? Если отсутствуют методы begin() и end()? Ну, я никогда не встречал чего-либо вроде контейнера, по которому нельзя проходить, поэтому мы можем определить отдельные begin() и end(). Стандартная библиотека предоставляет такую возможность для массивов С-стиля, поэтому если Container – массив из С, проблема решена.

3.2 Адаптация

Случай посложнее: что, если Container содержит указатели на объекты, и у него другая модель для доступа и прохода? К примеру, к нему надо обращаться так:

for (auto p = c.first(); p!=nullptr; p=c.next()) { /* сделать что-либо с *p */} 


Такой стиль не редок. Его можно привести к виду последовательности [b:e) вот так:

template<typename T> struct Iter { 
  T* current; 
  Container<T>& c; 
}; 
template<typename T> Iter<T> begin(Container<T>& c) { return Iter<T>{c.first(),c}; } 
template<typename T> Iter<T> end(Container<T>& c) { return Iter<T>{nullptr,c}; } 
template<typename T> Iter<T> operator++(Iter<T> p) { p.current = p.c.next(); return p; } 
template<typename T> T* operator*(Iter<T> p) { return p.current; } 


Такая модификация неагрессивна: мне не пришлось изменять Container или иерархию его классов, чтобы привести его к модели прохода, поддерживаемой стандартной библиотекой С++. Это адаптация, а не рефакторинг. Я выбрал этот пример для демонстрации того, что такие техники обобщённого программирования не ограничены стандартной библиотекой. Кроме того, они не попадают под определение «ОО».

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

4. Миф 3: В надёжных программах необходима сборка мусора


Сборка мусора хорошо, но не идеально справляется с возвратом неиспользуемой памяти. Это не панацея. Память может оказаться занятой не напрямую, а множество ресурсов не являются только лишь памятью. Пример:

class Filter { // принять ввод из файла iname и вывести результат в файл oname 
  public: 
    Filter(const string& iname, const string& oname); // конструктор 
    ~Filter(); // деструктор 
  // … 
  private: 
    ifstream is; 
    ofstream os; 
  // … 
}; 


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

void user() 
{ 
  Filter flt {“books”,”authors”}; 
  Filter* p = new Filter{“novels”,”favorites”}; 
  // использовать flt и *p 
  delete p; 
} 


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

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

Общепринятый и рекомендуемый подход в С++ — полагаться на деструкторы, чтобы удостовериться, что ресурсы возвращены. Обычно ресурсы забирают в конструкторах, что даёт этой технике имя «Получение ресурсов – это инициализация» (“Resource Acquisition Is Initialization”, RAII)). В user() деструктор flt неявно вызывает деструкторы потоков is и os. Они, в свою очередь, закрывают файлы и выпускают ресурсы, связанные с потоками. delete сделал бы то же самое для *p.

Опытные пользователи современного С++ заметят, что user() неуклюж и подвержен ошибкам. Так было бы лучше:

void user2() 
{ 
  Filter flt {“books”,”authors”}; 
  unique_ptr<Filter> p {new Filter{“novels”,”favorites”}}; 
  // используем flt и *p 
}


Теперь по выходу из user() *p автоматически освобождается. Программист не забудет этого сделать. unique_ptr – класс стандартной библиотеки, который удостоверяется, что ресурсы освобождены, без потери в производительности и памяти, по сравнению со встроенными указателями.

Хотя и это решение чересчур многословно (Filter повторяется), и разделение конструктора обычного указателя (new) и умного (unique_ptr) требует оптимизации. Можно улучшить это через вспомогательную функцию С++14 make_unique, которая создаёт объект заданного типа и возвращает указывающий на него unique_ptr:

void user3() 
{ 
  Filter flt {“books”,”authors”}; 
  auto p = make_unique<Filter>(“novels”,”favorites”); 
  // используем flt и *p 
} 


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

void user4() 
{ 
  Filter flt {“books”,”authors”}; 
  Filter flt2 {“novels”,”favorites”}; 
  // используем flt и flt2 
} 


Короче, проще, понятнее, и быстрее.

Но что делает деструктор Filter? Освобождает ресурсы Filter – закрывает файлы (вызывая их деструкторы). Это делается неявно, поэтому если от Filter более ничего не нужно, можно избавиться от упоминания его деструктора и дать компилятору сделать всё самому. Поэтому, всего-навсего нужно написать:

class Filter { // принять ввод из файла iname и вывести результат в файл oname 
  public: 
    Filter(const string& iname, const string& oname); 
    // … 
  private: 
    ifstream is; 
    ofstream os; 
    // … 
}; 

void user3() 
{ 
  Filter flt {“books”,”authors”}; 
  Filter flt2 {“novels”,”favorites”}; 
  // используем flt и flt2 
} 


Эта запись проще большинства записей из языков с автоматической сборкой мусора (Java, C#), и в ней нет утечек из-за забывчивости. Она также быстрее очевидных альтернатив.

Это – мой идеал управления ресурсами. Он управляет не только памятью, но и другими ресурсами – файлы, потоки, блокировки. Но на самом ли деле он всеобъемлющий? Что насчёт объектов, у которых нет одного очевидного владельца?

4.1 Передача владельца: move

Рассмотрим проблему передачи объектов между областями видимости. Вопрос в том, как вывести кучу информации из области видимости, без ненужного копирования или подверженного ошибкам использования указателей. Традиционно используется указатель:

X* make_X() 
{ 
  X* p = new X: 
  // … заполнить X … 
  return p; 
} 
void user() 
{ 
  X* q = make_X(); 
  // … использовать *q … 
  delete q; 
} 


И кто ответственный за удаление объекта? В нашем простом случае – тот, кто вызывает make_X(), но в общем случае ответ не так очевиден. Что, если make_X() кеширует объекты для минимизации использования памяти? Если user() передал указатель на other_user()? Много где можно запутаться и при таком стиле программирования утечки нередки. Можно было бы воспользоваться shared_ptr или unique_ptr для непосредственного определения владельца объекта:

unique_ptr<X> make_X(); 


Но зачем вообще использовать указатель? Часто он не нужен, часто он отвлекает от обычного использования объекта. К примеру, функция сложения Matrix создаёт новый объект, сумму, из двух аргументов, но возврат указателя привёл бы к странному коду:

unique_ptr<Matrix> operator+(const Matrix& a, const Matrix& b); 
Matrix res = *(a+b); 


Символ * нужен для того, чтобы получить объект с суммой, а не указатель. Что мне реально нужно – объект, а не указатель на него. Мелкие объекты быстро копируются и я не стал бы использовать указатель:

double sqrt(double); // функция квадратного корня 
double s2 = sqrt(2); // получить квадратный корень из двух


С другой стороны, объекты, содержащие кучу данных, обычно являются обработчиками этих данных. istream, string, vector, list и thread – все они используют всего несколько байт для доступа к данным гораздо большего размера. Вернёмся к сложению Matrix. Что нам нужно:

Matrix operator+(const Matrix& a, const Matrix& b); // вернуть сумму a и b
Matrix r = x+y; 


Легко:

Matrix operator+(const Matrix& a, const Matrix& b) 
{ 
  Matrix res; 
  // … заполняет res суммами … 
  return res; 
}


По умолчанию, происходит копирования элементов res в r, но так как res будет удалён и его память освобождается, их копировать не нужно: можно «украсть» элементы. Это можно было сделать с первых дней С++, но это было сложно реализовать и технику понимал не каждый. С++11 поддерживает «воровство представления» напрямую, в виде операций move, передающих владение объектом. Рассмотрим простую двумерную матрицу из элементов типа double:

class Matrix { 
  double* elem; // указатель на элементы 
  int nrow; // количество строк
  int ncol; // количество столбцов
public: 
  Matrix(int nr, int nc) // конструктор: разместить элементы
    :elem{new double[nr*nc]}, nrow{nr}, ncol{nc} 
  { 
    for(int i=0; i<nr*nc; ++i) elem[i]=0; // инициализация
  } 
  Matrix(const Matrix&); // Конструктор копирования
  Matrix operator=(const Matrix&); // Копирование присваиванием 
  Matrix(Matrix&&); // конструктор перемещения
  Matrix operator=(Matrix&&); // конструктор присваивания
  ~Matrix() { delete[] elem; } // деструктор: освобождает элементы
  // … 
}; 


Операция копирования распознаётся по &. Операция перемещения – по &&. Операция перемещения должна «украсть» представление и оставить позади «пустой объект». Для Matrix это означает нечто вроде:

Matrix::Matrix(Matrix&& a) // переместить конструктор
:nrow{a.nrow}, ncol{a.ncol}, elem{a.elem} // “украсть” представление 
{ 
  a.elem = nullptr; // ничего не оставить позади
} 


Вот и всё. Когда компилятор видит return res; он понимает, что res скоро будет уничтожен. Он не будет использоваться после return. Тогда он применяет конструктор перемещения вместо копирования для передачи возвращаемого значения. Для

Matrix r = a+b; 


res внутри operator+() становится пустым. Деструктору остаётся совсем мало работы, а элементами res теперь владеет r. Мы получили элементы результата (это могли быть мегабайты памяти) из функции (operator+()) в переменную. И сделали это с минимальными затратами.

Эксперты С++ указывают, что в некоторых случаях хороший компилятор может полностью устранить копирование для возврата. Но это зависит от их реализации, и мне не нравится, что быстродействие простых вещей зависит от того, насколько умный попался компилятор. Более того, компилятор, устраняющий копирование, может устранить и перемещение. У нас здесь простой, надёжный и универсальный способ устранения сложности и затрат по перемещению большого количества информации из одной области видимости в другую.

Кроме того, семантика перемещений работает и для присваивания, поэтому в случае

r = a+b; 


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

Частенько нам даже не придётся определять все эти операции копирования и перемещения. Если класс состоит из членов, которые ведут себя, как положено, мы можем просто положиться на операции по умолчанию. Пример:

class Matrix { 
  vector<double> elem; // элементы
  int nrow; // количество строк
  int ncol; // количество столбцов
public: 
  Matrix(int nr, int nc) // constructor: allocate elements 
    :elem(nr*nc), nrow{nr}, ncol{nc} 
  { } 
  // … 
}; 


Этот вариант ведёт себя так же, как предыдущий, кроме того, что он лучше обрабатывает ошибки и занимает чуть больше места (вектор – это обычно три слова).

Как насчёт хендлов, которые не являются обработчиками? Если они небольшие, типа int или complex, не беспокойтесь. В ином случае, сделайте их обработчиками или возвращайте их через умные указатели unique_ptr и shared_ptr. Не пользуйтесь «голыми» операциями new и delete. К сожалению, Matrix из примера не входит в стандартную библиотеку ISO C++, но для него есть несколько библиотек. Например, поищите «Origin Matrix Sutton» и обратитесь к 29 главе книги The C++ Programming Language (Fourth Edition) за комментариями по её реализации.

Часть 2
Метки:
Поделиться публикацией
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 114
  • +3
    Ура, Ура, Ура. Сделали перевод Страуструпа! Обсуждение на reddit:
    Five Popular Myths about C++, Part 1
    Five Popular Myths about C++, Part 2
    Five Popular Myths about C++, Part 3
    • +2
      Да не то слово. У Страуструпа совершенно неповторимый стиль письма, читать его — почти наслаждение. Читаешь и как бы представляешь себе убеленного сединами дзен-старца, сидящего напротив и неспешно рассказывающего тебе притчи за чашкой чая. С одной стороны, все разжевано, но с другой — не чрезмерно, а в самый раз, и даже веет некоторым академическим духом. Как он так умудряется, непонятно.

      Что касается смысловой части моего комментария, то я рекомендую всем, кто не читал его монументальный труд по C++, сделать это, даже если вы никогда не писали ранее на C++, а пишете на Java, Python, PHP и т.д. Купите в магазине бумажную версию на русском языке, я обещаю — получите массу удовольствия.
      • +3
        … да, я еще хочу добавить, что Страуструп — мужик уникальный. Ниже было сравнение с Виртом и замечено, что Вирт в конце заскакал галопом по европом и забросил все свои языки. Может быть, это связано с возрастом — седина в бороду, старческий склероз и т.д. А вот Страуструп взвалил на себя и все эти годы тянет такую огромную МАХИНУ… И ни намека на забрасывание. Я восхищаюсь тем, как у него эта махина даже просто помещается в голове. Ведь C++ — сложнейший язык (наверное, самый сложный из существующих?), и ведь все так построено, что его сложность реально оказывается оправданной (это при наличии-то кучи других, более простых языков вокруг). И он не коллапсирует под собственным весом, возможно, благодаря усилиям Страуструпа.
        • +4
          Я восхищаюсь тем, как у него эта махина даже просто помещается в голове.

          Ну, по признанию Страуструпа он знает язык на 7-8 из 10ти… Так что — не совсем уже помещается :)
      • +8
        Зашёл в предвкушении почитать холивар согласчных и не согласных, но поняв, что это перевод Страуструпа… вообщем не стоило запасаться поп-корном…
        • +2
          Да уж, я тоже ближе к середине статьи начал чувствовать неладное: скепсис постепенно улетучивается, а ладони сами собой складываются на груди в молебную лодочку. Думаю, вот это автор жжет. А потом смотрю — это ж памятник Страуструп…
        • +1
          А я все-таки подкину холивора к переводу:
          миф 1; с использованием getchar(), isdigit() можно написать то, что будет быстрее против «полноценного» iostream.
          (и да, в iostream внутри где-то была глобальная блокировка… вокруг разных кодировок, что ли? неважно, оно и так медленно).
          миф 5: для таких задач есть perl. Для промежуточных задач есть perlembed. Не надо писать небольшие программы на C++, он недружелюбен к маленьким юзерским задачам.
          • +3
            миф 1; с использованием getchar(), isdigit() можно написать то, что будет быстрее против «полноценного» iostream.
            (и да, в iostream внутри где-то была глобальная блокировка… вокруг разных кодировок, что ли? неважно, оно и так медленно).

            Статья ж про это была на хабре меньше месяца назад. Там и сравнение было по скорости выполнения.
        • +4
          В С-версии необходимо напрямую работать с символами и памятью:

          Ну, для таких задач есть snprintf/asprintf, как мне кажется.
          • 0
            Аналогом может быть только функция с сигнатурой
            char * concat(const char *, const char *, ...);
            

            Но такой функции нет во-первых, а во-вторых в c++ можно сконкатить любой тип, для которого есть конструктор… В общем много чего прийдется делать ручками, без дополнительных библиотек.
          • +34
            Вброшу немного, а то народ при виде Страуструпа как-то поник.

            Благодаря С++11, С++ стал более дружественным для новичков.

            А давайте всё-таки сжалимся над новичками? Что си, что плюсы — отвратительные языки для обучения программированию. Вот в коде для плюсов используется const string& в качестве аргументов для конкатенации. Что вы ответите на вопросы новичка «что такое const» и «и что такое &»?

            Приведённый код на C более длинный, но он примитивный, состоит из простых кирпичиков. Объяснить его гораздо проще. Примитивную реализацию strlen и strcpy даже показать можно. Никакой магии. То есть если припрёт обучать относительно низкоуровневому коду, но С как раз подходит лучше.

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

            Прекрасное обобщение, ничего не добавить.

            Миф 3: В надёжных программах необходима сборка мусора

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

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

            В плюсах тьма фич для управления памятью. При использовании любой из них очень просто допустить ошибку. Ну и где доказательство надёжности для опровержения мифа?
            • +23
              Да как вы посмели возражать Страуструпу?!
              • +12
                C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off.
                • +4
                  Приведённый код на C более длинный, но он примитивный, состоит из простых кирпичиков. Объяснить его гораздо проще. Примитивную реализацию strlen и strcpy даже показать можно. Никакой магии. То есть если припрёт обучать относительно низкоуровневому коду, но С как раз подходит лучше.


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

                  Ну и где доказательство надёжности для опровержения мифа?

                  Само понятие, критерий надежности, автором не определено, так что доказательство/спор бессмысленны.
                  • +3
                    С вбросом частично согласен.

                    Читал недавно Мейерса. Эффективный С++14.
                    Таки я считаю что для новичка это далеко не лучший язык (сам начинал именно с него и много страдал). Да, const, & — по началу весьма непонятно (хотя, нет, вру, как раз const как таковой не вызывал у меня вопросов в параметрах, мне не понятно было зачем его пихать везде. И что значит const после прототипа функции.).

                    «вызов виртуальной функции препятствует инлайнингу, что может раз в 50 замедлить вашу программу»
                    Для тех кто плохо знаком с внутренней кухней это «хрмбрмрмрбхппхх», однозначно! :)

                    А вот насчет управления памяти и жизнью объектов с вами не соглашусь. Для меня — то что компилятор САМ позаботится о деструкторах, и есть «автоматически», а не вручную. Я пишу на плюсах 10 лет, из них 4 года в коммерческой разработке (нет, не такой большой опыт, кто спорит), но я уже давно не заморачиваюсь «управлением памятью».
                    Контейнеры, shared-поинтеры, передача this в качестве parent в Qt — все это разные ипостаси RAII, и я не знаю как при любой из них «просто допустить ошибку». Да, ошибки допустить МОЖНО. С++ позволяет допускать ошибки (например, создать указатель и передать его в конструкторы разных shared_ptr), но это же постараться надо =)

                    Т.е., подытожив — в современных плюсах РУЧНОЕ управление памятью есть, но оно нафиг обычно не нужно.
                    • +5
                      > новичкам про что такое const и &

                      const — гарантия неизменения состояния объекта
                      & — работа непосредственно с передаваемым экземпляром, без копирования

                      ?

                      Мне нравились все эти штуки, когда я изучал плюсы.
                      • +6
                        Вам нравятся эти штуки? Ок, продолжим.

                        std::string hello("Hello");
                        std::string world("world!");
                        
                        hello + " world!";  // ok
                        "Hello" + world;    // ok
                        "Hello" + " world"; // ERROR
                        

                        Ладно, сложение строковых литералов это боян. Давайте попробуем свежачок.

                        // ok
                        std::array<int, 5> int_array = {1, 2, 3, 4, 5};
                        
                        struct IntPair { int first; int second; };
                        
                        // ERROR, WTF?!!!
                        std::array<IntPair, 2> int_pair_array = {{0,0}, {100,100}};
                        
                        //ok
                        std::array<IntPair, 2> int_pair_array2 = {{{0,0}, {100,100}}};
                        
                        • 0
                          Воу,
                          too many initializers
                          ? Какого чёрта?
                          • +3
                            Это потому что std::array является аггрегатом, у него вообще нету пользовательских конструкторов (в том числе и конструктора, принимающего std::initializer_list). Этот код аналогичен такому (кстати, в вышеприведенном коде не указана длина массивов, так что второй ok на самом деле не ok):
                            struct array_type {
                                IntPair data[5];
                            };
                            
                            array_type int_pair_array = {{0,0}, {100,100}};
                            

                            Раз std::array является аггрегатом, то при инициализации с помощью фигурных скобочек происходит aggregate initialization. А она смотрит, что внутри две пары фигурных скобок, а член данных у аггрегата только один (массив data), и поэтому выдает ошибку компиляции.
                            • +3
                              Спасибо за объяснение. У меня вопрос: то, как Вы это поняли — это результат уже опыта, или где-то прочитали, или прочитали && скомбинировали && догадались?
                          • 0
                            Вот еще хороший боян, который без экскурса в си ( или скорее в асм ) объяснить будет непросто.
                            // ok
                            switch(x){ case 3: }; 
                            
                            // oops
                            std::string s;
                            switch (s) { case "3":};
                            
                            • +2
                              Объяснить-то можно очень просто: нежелание комитета по стандартизации ломать стереотип «switch компилируется в очень эффективный код», а также делать синтаксис ещё более монструозным. Я положительно не вижу причин, почему
                                  std::string s;
                                  switch (s)
                                  {
                                  case "1":
                                      something;
                                  case "2":
                                      another_thing;
                                  }
                                  // ...
                              
                              нельзя компилировать в
                                  std::string s;
                                  if (s == std::string("1"))
                                      goto case_1;
                                  else if (s == std::string("2"))
                                      goto case_2;
                                  else
                                      goto case_end;
                              case_1:
                                  something;
                              case_2:
                                  another_thing;
                              case_end:
                                  // ...
                              
                              Но ведь обязательно найдётся человек, которого operator==() не устроит, для которого надо будет выдумывать поддержку
                              switch <[](auto x, auto y){ return x.foo == y.bar; }> (s)
                              {
                              case zog:
                                  // ...
                              }
                              • –5
                                Только вот switch и string немного не дружат :)
                            • 0
                              Имху, костыль, да, груз совместимости. Но он мне тоже нравится. Если очень хочется, можно переопределить оператор для сложения строк. (может быть даже, в бусте есть для этого заголовочный файл, хотя есть риск что-то отломать). А в одном из новых стандартов мб есть что-то для компактного инплэйс создания константных std::string вместо const char[].

                              А еще для меня все это выглядит логично. Складывать char* нельзя, если хочешь складывать — пользуешься std::string. При этом я не вижу в использовании std::string чего-то языкового. Это, хоть и стандартная, но библиотека, и имеет свои семантические границы.

                              Свежачок заставил задуматься. Мне, на самом деле, не очень понятна суть list initialization (я про них почти не смотрел). Но, если бы код писал я, я писал бы
                              std::array<IntPair, 2> int_pair_array = {(IntPair){...}, (IntPair){}}
                              

                              То что иногда можно не писать тип перед {}, это, вроде, скорее приятное исключение. (как в составе = { })
                              • +3
                                Если очень хочется, можно переопределить оператор для сложения строк.
                                Операторы можно переопределять только для пользовательских типов; std::string + (char *) или (char *) + std::string OK, (char *) + (char *) нельзя.
                            • +4
                              const — гарантия неизменения состояния объекта
                              & — работа непосредственно с передаваемым экземпляром, без копирования


                              А зачем это знать при начальном обучении? Старый, прости господи, фортран, передавал все переменные по ссылке, так что они сквозные в каждой процедуре. Это позволяет (сама концепция, не язык) объяснять принципы программировани без размена на детали.
                              Вот потом уже можно рассуждать о том, вот при таком-то неудачном использовании можно случайно поменять этот объект, а чтобы такого не было, защитим его const'ом.

                              Со ссылками и указателями аналогично — это детали низкого уровня. (еще отличия от передачи по ссылке и указателю кроме синтаксиса) Что проще новичку:

                              abc(a, b)

                              a = b + 3

                              или объяснение про передачу с использованием указателя и почему мы не можем поменять a напрямую, если нам надо?
                            • +8
                              Си++ как первый язык плох уже тем, что новичок не понимает зачем си++ решает те или иные задачи, так как не писал на голом си.
                              А вот если сперва набить шишек на всех этих malloc/realloc/free, на закытии файлов/сокетов и тд, вот тогда начинаешь ценить деструкторы, конструкторы копирования и умные указатели. Да и глупых ошибок с присовоенем одного указателя двум разным объектам умных указателей уже не возникает.
                              Учить же с++ как первый — взырв мозга. Слишком много концепций, тонкостей, исключений, разных подходов ( например смесь cout и printf в коде ).
                              • 0
                                Согласен с вами. Без экскурса в историю о том, что C++ появился из Си, обычно не так просто объяснить, почему, например, scanf не работает с std::string.
                                • +1
                                  Читаю Вас Всех, и честно — не понимаю. Кратко про себя: {Школа -> Basic} + {Институт -> Pascal} + {практика -> С}. Без всякого предварительного изучения нужно было читать программы на C. Пришлось разбираться на месте. Я, если честно до мозга и костей проникся любовью к скобкам{} /инкременту ++/декременту --/то, что переменные нет необходимости объявлять заранее/к оператору return и пр. Потом С++ изучал из собственного любопытства. Дальше работа. Угадаете на каком языке пишу?
                                  Вот за весь этот путь я ни разу не оглянулся и не сказал: «какой сложный язык! А вот в Delphi/Java/C# бла-бла-бла на Ваш вкус». Может тут не все отлавливают 45% ошибок еще на этапе компиляции, а еще 50% во время первых отладок? Оставшиеся 5% это явно не проблема языка и все те, кто занимается разработкой хотя бы лет 5, это понимают.

                                  Подытожу: сложности и нюансы есть в любом языке. Вы конечно можете дать новичку пример со строками, как выше, только у меня есть один вопрос — а зачем новичку это показывать? Или может быть в других языках нет операций взятия адреса или спецификатора const? Почему, если произносятся 2 слова в одном предложении — «новичок + изучать», то все сразу думают про школу/институт/первый язык программирования? Как насчет тех, кому просто необходимо изучить C++?
                                  • 0
                                    Или может быть в других языках нет операций взятия адреса или спецификатора const?

                                    Не во всех есть. В том же Бейсике нет. В паскале не помню, есть ли const.

                                    о все сразу думают про школу/институт/первый язык программирования?

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

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

                                      Вот за весь этот путь я ни разу не оглянулся и не сказал: «какой сложный язык! А вот в Delphi/Java/C# бла-бла-бла на Ваш вкус».

                                      Мой путь: {Школа: Logo, C, C++} -> {Универ: Pascal, Delphi, PHP, SQL, JS, C++} -> {Практика: C++, C#}. Уверяю, начинать с плюсов — это не самая лучшая идея. :) Хотя я тоже полюбил скобочки, инкременты и прочее, это была боль. И потом понимаешь, что можно было проще.
                                    • +3
                                      Полагаю такую историю может рассказать почти каждый из отметившихся в этом топике. Дело ведь не в том как вам или мне удалось разобраться и продраться через трудности ( о которых тогда возможно и не подозревали ). А в том какой ЯП будет наиболее оптимальным для начального изучения. Я вот тоже начал программировать на си++ до того как изучил его, и разбирался по примерам и говноучебникам вида: «выучи си++ за 21 день», с примерами для шестой студии. И я далеко не сразу смог разделить у себя в голове си, си++ и вин-апи, так как подавалось все скопом. Уже с годами знания выкристализовались, разложились, да и качество читаемой литературы выросло. Так вот я уверен что было бы лучше учить языки последовательно, не смешивая из в эдакий дикий коктейль.
                                • +8
                                  > 5. С++ подходит только для больших и сложных программ
                                  Чтобы опровергнуть это, страуструп говорит, что есть много библиотек, а Hello world делается тремя строками.
                                  Вот только он забыл написать что-то на тему якобы мифа.

                                  Нам стоит верить тому, что он говорит (вбрасывает???) без всяких доводов и доказательств, потому что именно он Страуструп, а не мы?
                                  • +4
                                    Я вообще в восторге от того, как он «доказал» пригодность плюсов для простых задач, написав программу для поиска регекспом ссылок на страничке длиной в 60 строк (!), несмотря на использование последнего стандарта и буста. Ручная сброка GET-запроса и парсинг ответа прилагается.
                                    • +1
                                      Задача действительно простая, 60 строк — немного. Что не так?
                                      • +3
                                        То, что на языке, имеющем полноценную стандартную библиотеку, этот код займёт 3 строки (ну, плюс ещё строк пять, если язык исповедует «труЪ ООП»).
                                        • 0
                                          Даже если мы создадим язык, в котором операция «получить файл, пройтись регэкспом и выдать массив» будет встроенной, т.е. реализация займет одну строку, это ничего не докажет. Это специфическая задача и ничего не показывает об универсальном языке программирования, под специфическую задачу не заточенном.

                                          Это — не стандартная задача.
                                          • +5
                                            Работа с HTTP — стандартная задача. Если в коде приложения вручную собираются HTTP-запросы — это ненормально.
                                            • +3
                                              Настолько ли стандартная, чтобы быть в стандартной библиотеке?

                                              Да, функция вида «получить файл по адресу в строку» наверняка где-то реализована и вызывается тоже в одну строчку. При помощи той же WinHTTP если речь о Win32, реализуется просто. То, что автор не стал искать такую библиотеку — другой вопрос. Это вопрос «поколений» — возможно, он хотел сказать, что, мол, даже такая сложная (по его мнению) задача как вот эта реализуется так просто. Считать работу с HTTP тривиальной, простой или сложной задачей можно по-разному, в зависимости от основной сферы деятельности. Если, например, кто-то в основном занимается разработкой под микроконтроллеры, и для него работа с HTTP — ручная, сложная вещь, то демонстрация С. будет восприниматься именно как «ого, действительно просто». Например, просто за счет использования класса строк вместо работы со строками в С стиле.
                                              • +5
                                                К вопросу о стандартности HTTP и включению в стандартную библиотеку. Запилил вопрос на CodeGolf.StackExchange:

                                                Debunking Stroustrup's debunking of the myth “C++ is for large, complicated, programs only”

                                                Сейчас там будет полный список языков, у которых в стандартной библиотеке есть HTTP. С примерами кода. :)
                                                • +2
                                                  Если не ограничиваться стандартной библиотекой, то вскрывается еще один минус С++ — подключение внешней библиотеки не всегда просто. Просто подключаются только header only библиотеки шаблонов, при условии, что ваш компилятор эти шаблоны переварит. Хорошо, если вы найдете готовые бинарники, подходящие вам. Часто приходится собирать все самому и тут может оказаться, что библиотека просто не собирается вашим компилятором и надо либо искать что-то другое, либо модифицировать код библиотеки.
                                                  • 0
                                                    На самом деле не совсем так. На линуксе есть пакетные менеджеры, если библиотека есть в репозитории все ок.

                                                    Если нет, можно сделать make install.

                                                    Если библиотека использует ту же систему сборки, что и ваш проект ее можно тривиально включить в сборку (актуально как минимум для CMake и autotools).

                                                    Если библиотека вообще не собирается на вашей архитектуре/версии компилятора, то это либо плохая библиотека, либо повод сделать патч :)
                                                    • 0
                                                      Линукс — не единственная система, под которую пишут код на С++. Не везде есть пакетные менеджеры.
                                                      • 0
                                                        Ок, я в последнее время больше связан с линуксами и маками, поэтому пишу про то, что знаю.

                                                        В самом деле еще остались системы без пакетных менеджеров? Я слышал для винды что-то в этом плане делалось. Плюс пакетным менеджером возможности не ограничиваются (ссылка на стандартные системы сборки).
                                                        • 0
                                                          Делалось, появилось в Windows 10, но пока вроде только для программ. И то пока не для всех. И то не помню, с удалением или без.
                                                          • 0
                                                            NuGet уже давно поддерживает C++.
                                                      • +1
                                                        Насколько я понимаю, CMake нормально переваривает любую систему сборки. Во всяком случае, в neovim (единственный проект, который я знаю, который не считает, что зависимости должны устанавливаться пакетным менеджером) как‐то с этим нормально справляются.

                                                        make install делать не рекомендуется, если только вы ставите не в каталог, который потом будете подключать через LD_LIBRARY_PATH и который содержит либо только одну конкретную зависимость, либо только зависимости для одного конкретного проекта (соответственно, в обоих случаях его можно снести, зная, что именно при этом поломается и что именно вы сносите) — make uninstall далеко не всегда есть. Тот же CMake не генерирует его по‐умолчанию.
                                                    • 0
                                                      Отлично, посмотрим, сколько языков из более-менее современных (эпоха http) имеют в стандартной библиотеке работу с http. И сколько таких языков вообще, а потом посмотрим долю.
                                                      • +1
                                                        В итоге получается неравноценное соревнование: соревнуются языки, которые поддерживают работу с HTTP с C++ в целом, серьёзно? А то, что на C++ можно «эмулировать» работу любого языка, который есть в том списке — не берётся в счёт?
                                                        А теперь ответы:

                                                        Python 2: где обработка ошибок? А в остальном — да — отличная работа с HTTP.
                                                        C#: угумс, но сначала поставим .NET для Windows и что-то ещё для других платформ, где, по рассказам, реализация ещё глючная (я не говорю, что для C++ ничего не надо ставить — просто C++ это проще).
                                                        UNIX shell: оо, прям кроссплатформенное решение, вместе с использованием кроссплатформенных утилит lynx и grep. С таким же успехом на C++ можно написать утилиту show_straustrups_lonks и в одну строчку выполнить её.
                                                        PHP 5: очень смешно наблюдать, как язык который заточен под работу с web-технологиями сравнивается с С++ в возможности закачивать файлы. Но ладно, сначала поставим какой-нибудь веб-сервер, который, скорее всего, написан на С++ — где можно будет запустить скрипт (правильно я понимаю, как работает PHP ?)
                                                        Scala: нельзя придраться — функциональное программирование и всё такое. Разве что C++ поддерживает, кроме всего прочего, и функциональное программирование.
                                                        Node.js: то же самое, что и PHP — нет смысла соревноваться
                                                        Haskell: снова функциональное программирование сравнивается с императивным. Да, и ещё Haskell популярный (извините заранее, никого не хочу задеть)
                                                        CJam: «CJam does not have regex»… «Try it using the Java interpreter like java -jar cjam-0.6.2.jar file.cjam»… — Очень удобно

                                                        Просто хочу сказать, что каждый язык имеет свою нишу. И что-то, что в одном языке делается просто, то в другом — это делается или монструозно, или вообще не делается (можно ли с javascript вызвать системную функцию, например ?)
                                                        • 0
                                                          можно ли с javascript вызвать системную функцию, например
                                                          легко
                                                          • 0
                                                            Вот здесь написано:
                                                            JavaScript does not have any facilities to interact with the OS

                                                            А здесь:
                                                            JavaScript runs in a sand-box, meaning that it cannot interfere with the user's computer

                                                            А с помощью Child Process можно запустить дочерний процесс.
                                                            Я не знаю JS, но, для общего развития, как вызвать CreateProcess(), например?
                                                            • 0
                                                              А разве JS нужен для того чтобы вызывать системные вызовы? Если Вы в своем проекте что-то подобное пытаетесь делать, то Вы видимо не выспались!
                                                              • +1
                                                                Нет. Вы читали комментарии выше? Я сказал «С javascript нельзя вызвать системную функцию» («каждый язык имеет свою нишу» — javascript не для вызова системных функций). Мне в ответ написали — «легко». Я попросил показать на примере
                                                              • +2
                                                                JavaScript это такой-же язык как любой другой. Возможности зависят от среды выполнения — всегда есть внешние функции (которые сами реализованы не на JS) которые можно вызывать из JS. Например — функции для работы с DOM, CSSOM и объектной моделью самого браузера.

                                                                Движок выполнения JS поддерживает внешние функции; конкретные возможности зависят от набора «прокинутых» функций. В браузере JS выполняется в «песочнице», возможности сознательно ограничены.

                                                                Node.js — это технология для разработки сервера на JavaScript, там возможности намного шире.

                                                                Любопытно кстати, что C++ тоже можно аналогично «кастрировать». У google есть технология NaCl (NativeClient) — выполнение нативного кода в безопасной песочнице в браузере у пользователя, аналогично JS. В первой версии песочница создавалась средствами ОС, а C++ был почти обычный. Это уже потом они сделали компиляцию в промежуточный байткод и другую магию.
                                                                • +2
                                                                  Всё зависит где запущен JS, если в браузере то никак, если в NodeJS то пишешь свой модуль на C++ и что хочешь вызывай.
                                                                  ИМХО JS язык встраиваемый(как например и Lua) куда встроишь там и будет работать, хоть в ядро запихни и железом управляй.
                                                                  • 0
                                                                    если в NodeJS то пишешь свой модуль на C++ и что хочешь вызывай.

                                                                    Ну, это уже то же что и «хочешь http в c++ — пиши свой модуль и вызывай», так что несчитово.
                                                                    • +3
                                                                      В конечном итоге для других языков кто то написал библиотеку для работы с http, и рано или поздно корни этой библиотеки упрутся к какой либо компилируемый язык. Да где то сделали готовую библиотеку для работы с тем же HTTP но как это связано с самим языком?

                                                                      Для примера, у меня на работе валяется самописный модуль для NodeJS которые позволяет читать/писать память приложения по его PID. Реализация ну строк 100 от силы, причём процентов 70 это инициализация всякая. Есть ещё куча мелких программ на NodeJS для парсинга всяких хитрых бинарных файлов в JSON. Но как это связано с самим JavaScript?
                                                                      • 0
                                                                        Да где то сделали готовую библиотеку для работы с тем же HTTP но как это связано с самим языком?
                                                                        Неправильно рассматривать язык отдельно, а библиотеки отдельно.
                                                                        Это очень удобно, когда вместе с языком ставится стандартная библиотека, которая решает 95% повседневных задач. Как в питоне например.

                                                                        Давайте проведем эксперимент — посоветуйте мне библиотеку для работы с HTTP в С++?

                                                                        Я уверен, предложите десяток вариантов. А мне нужен ровно один. И чтобы не было проблем с деплоем.

                                                                        На всякий случай напомню контекст — Страуструп утверждает, что C++ — разумный выбор, когда требуется написать маленькую одноразовую утилиту («Five Popular Myths about C++; 5. C++ is for large, complicated, programs only»). Так именно на таких задач особенно важна богатая стандартная библиотека.
                                                                        • +3
                                                                          Неправильно рассматривать язык отдельно, а библиотеки отдельно.
                                                                          Это очень удобно, когда вместе с языком ставится стандартная библиотека, которая решает 95% повседневных задач. Как в питоне например.

                                                                          ИМХО язык это грубо говоря синтаксические конструкции его образующие. Библиотеки это ощутимый бонус, и да это очень удобно. Но вот к языку отношения не имеет.

                                                                          Давайте проведем эксперимент — посоветуйте мне библиотеку для работы с HTTP в С++?
                                                                          Я уверен, предложите десяток вариантов. А мне нужен ровно один. И чтобы не было проблем с деплоем.

                                                                          Я не С++ник мне в голову приходит только curl он вроде C но на С++ его подключить не проблема.
                                                                          На всякий случай напомню контекст обсуждения — Страуструп утверждает, что C++ — разумный выбор, когда требуется написать маленькую одноразовую утилиту

                                                                          Если ты отлично знаешь С++ и отвратительно Python то разумный выбор будет С++. Всё относительно. И надо с начало понять что есть маленькая одноразовая утилита и для чего, вот мне надо было писать в память чужого приложения, утилита одноразовая и маленькая, и оптимально ложилась в С++. Но я использовал NodeJS + C++
                                                              • 0
                                                                Для C# среда уже предустановлена для свежих виндов, а вот для C++ рантайм надо ставить отдельно.
                                                                • 0
                                                                  Спасибо, что подытожили.

                                                                  Как и ожидалось, в общем: либо специализированные языки (PHP), либо небольшое количество универсальных (Шарп и др).
                                                                  Ничего такого, что позволяет кидаться фразами вида «в современных языках это делается в 3 строки» как аксиомами.
                                                                  • +2
                                                                    Python 2: где обработка ошибок?

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

                                                                    В любой приведённой программе достаточно обернуть весь код в try-catch, ловящий все исключения, и выдать сообщение исключения в консоль. Не нужно никакой особой обработки ошибок. (Допускаю, что где-то следовало бы проверять возвращённый код HTTP.)

                                                                    C#: угумс, но сначала поставим .NET для Windows

                                                                    Который на винде уже поставлен из коробки и регулярно обновляется с помощью Windows Update. На линуксе зависимость ставится как и любая другая, ноль отличий. «Глючность» (скорее мелкие отличия в поведении и некоторые нереализованные фичи) на таких программах неспособна проявиться.

                                                                    UNIX shell: оо, прям кроссплатформенное решение, вместе с использованием кроссплатформенных утилит lynx и grep.

                                                                    Не нравится lynx — используйте другой вариант с wget. Lynx там ради лулзов.

                                                                    PHP 5: очень смешно наблюдать, как язык который заточен под работу с web-технологиями сравнивается с С++ в возможности закачивать файлы.

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

                                                                    веб-сервер, который, скорее всего, написан на С++

                                                                    C++, первая версия которого написана на C, первая версия которого написана на ASM. Что вы хотели этим сказать-то?

                                                                    Разве что C++ поддерживает, кроме всего прочего, и функциональное программирование.

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

                                                                    Да, и ещё Haskell популярный (извините заранее, никого не хочу задеть)

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

                                                                    можно ли с javascript вызвать системную функцию, например ?

                                                                    Да. Язык никак это не ограничивает. Возьмите ноду, какую-нибудь реализацию для .NET или любую другую среду исполнения кроме браузера — и дерзайте.
                                                                    • +1
                                                                      show_straustrups_links

                                                                      Вообще, несколько языков всем утёрли нос своей стандартной библиотекой. :-D

                                                                      Wolfram:
                                                                      Import["http://www.stroustrup.com/C++.html", "Hyperlinks"]

                                                                      PowerShell:
                                                                      (iwr "http://www.stroustrup.com/C++.html").Links | %{ $_.href }
                                                                      • 0
                                                                        Вы полностью правы. Посыпаю голову пеплом. Может меня не правильно поняли, но я хотел сказать, что не нужно смотреть на язык в целом, смотря лишь на то, поддерживает ли стандартная библиотека работу с HTTP или нет. И да, я полностью согласен, что наличие такой библиотеки упростило бы жизнь C++-программиста. Но тогда бы уж хотелось и полноценную работу с файловой системой, возможность работы с теми же форматами (JSON, мейнстрим сейчас)… ну и каждый может добавить свою хотелку. Но, снова же, если говорить о том, что на С++ можно писать маленькие утилитки-программки, то да — можно, но нет — лучше поискать альтернативу — тот же питон или bash.
                                                                        Отдельное спасибо за вопрос на CodeGolf.StackExchange.
                                                                      • 0
                                                                        Но ладно, сначала поставим какой-нибудь веб-сервер, который, скорее всего, написан на С++ — где можно будет запустить скрипт (правильно я понимаю, как работает PHP ?)
                                                                        Нет. Веб сервер — приложение, которое постоянно слушает какой-то порт (обычно :80) в ожидании http запросов, и на основе этих запросов решает какой именно скрипт вызывать, и нужно ли его вообще вызывать, или же достаточно выдать в сеть картиночку с котиком или статический html файл. Плюс всякие полезные штуки типа кеширования. Это не только PHP касается, но вообще всех «веб»-языков.

                                                                        А сам PHP можно запускать из консоли (шеллскрипта/батника), или даже скомпилировать(!) в исполняемый файл.
                                                                  • +2
                                                                    Может это и ТИПОВАЯ задача, но я был бы сильно против, чтобы такие инструменты были в стандартной библиотеке языка.
                                                                    Ну и потом, Бьёрн иллюстрирует API конкретной библиотеки, asio — на Qt, мне кажется, я бы написал в два раза короче.
                                                                    • –1
                                                                      Qt ( по крайней мере в версиях 3 и 4 ) не очень-то то здорово работал с сетью. То есть для обычный гуи программы вполне приемлемо, но не эталонно хорошо.
                                                                      Больше всего меня бесило что создание объекта QSocket не создавало системный сокет. Сокет создавался лишь при вызове connect(). Такое вот улучшение, которое убивало прозрачность и предсказуемость кода.
                                                                      • 0
                                                                        Я сперва написал реализацию канала tcp под posix сокеты, а потом портировал под винду используя Qt. И мне почему-то не казалось нелогичным создание сокета только при коннекте. Более того, я считаю setSocketDescriptor очень неплохой штукой.
                                                                        Вообще изначальная ветка была про «многословность», так что оставим это =)
                                                                • +3
                                                                  Hа Borland Builder C++ это таки занимает одну строчку.

                                                                  Но потом работа таких програмистов сводится к поиску — где мне найти компонент бухгалтерия.
                                                          • +13
                                                            Я не понимаю всей этой паники по поводу обучения программированию новичков.

                                                            Кажется очевидным, что «язык, с которого хорошо начинать» — это не язык, в котором можно однострочником решить любую задачу, а наоборот — самый прямолинейный язык, в котором ни про одну конструкцию нельзя сказать «ну, это короче, очень сложно, потом разберём, на третьем курсе». Хороший язык для обучения — это язык, который ставит перед новичком максимальное количество препятствий, чтобы обойдя их уже не новичок:

                                                            а)Понимал, что «машина думает по-другому».
                                                            б)Знал, про какие стандартные грабли нужно помнить, разрабатывая на любом языке.
                                                            в)Понимал, что ни один синтаксический сахар не даётся бесплатно.

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

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

                                                            www.gazeta.ru/comments/2014/12/22_e_6355209.shtml
                                                            • +1
                                                              Я с Вами частично соглашусь, хотя бы потому что я как раз прошел это на своей шкуре. Начал изучать в 9 классе С++, долго матерился и бился упорно головой, писал в Си-стиле, не читал книжек, набивал шишки.
                                                              В итоге когда через год стал смотреть на Delphi, Javascript а через пару лет еще и PHP, то многие внутренние особенности этих языков уже не вызывали вопросов =)

                                                              с в) не соглашусь.
                                                              в С++ синтаксический сахар в виде шаблонов дается зачастую бесплатно. На хабре была статья ( увы нет в закладках), как человек на шаблонах под микроконтроллеры генерировал код, причем результирующий ASM был такой же оптимальный, как человек бы вручную соптимизировал.
                                                              inline template<> имеет такой же overhead, как и макрос, по сути.
                                                              • +3
                                                                Есть мнение, что нам просто не хватает достаточно строго разделение между, назовём так, «архитекторами» и «кодерами». Задача первых — собрать сложную систему из имеющихся, а порой и не имеющихся частей. Задача вторых — выполнить задание с бумажки. И этих людей обучать нужно по-разному.

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

                                                                Если честно, у меня в голове разделение «программистов» намного сложнее. Например, «программисту компьютерной графики» неплохо бы знать неслабый кусок линейной алгебры и представлять векторные операции (математические) в уме. «Системный программист» должен понимать ассемблер и способы, которыми высокоуровневый язык будет транслирован в процессорные инструкции. «Программист интерфейсов» должен смыслить ещё и в дизайне и UX, а не лепить то, что удобно будет только ему. И так далее.

                                                                Не может быть «серебряной пули», способа создать любой подвид программиста с помощью одного языка. Графику я бы начал преподавать с чего-нибудь простого, может даже паскаля или бейсика (потому, что их важно сначала обучить алгоритмам, язык должен быть с минимумом премудростей). Системщикам, очевидно, лучше давать голый си сразу же. А создатели интерфейсов будут начинать с javascript и HTML.

                                                                Конечно, никто не ограничен одной специализацией, но способ обучения будет уже другой. Сам я взялся сначала за си, потом и за C++ курсе на пятом обучения. Задачи были и раньше, но я делал только учебный план. На тот момент я уже был способен делать много чего как веб-программист. Последний раз я программировал делал что-то для веба (не на C++ :)) года 4 назад, недавно попытался понять AngularJS, за полдня заработал вывих мозга и отложил это дело. Не жалею, что начинал с JS и PHP, но, похоже, уже никогда к ним не вернусь. Специализация должна определять метод обучения, а не картинка «хорошие программисты получаются так».
                                                                • +3
                                                                  Мне кажется, что обучение программированию надо начинать, как ни странно, с программирования, а не с технологий и языков. Мы в свое время начинали с паскаля, который именно для обучения и был создан, это потом на него накрутили объекты и Delphi. Сначала на простом языке человек учится составлять вообще алгоритм, понимая принципы работы компьютера, разбирается с примитивными структурами данных. Этот курс, помню, занял год в школе, но это же арифметика, без которой не будет ни системного программиста, ни архитектора, ни кодера. И современные скриптовые языки, мне кажется, не очень подходят, потому что в них уже все это реализовано, и человек начинает мыслить примитивами языка, а не алгоритмами.

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

                                                                  А как человек решит, что он системщик или архитектор? Мне кажется, что базовый курс алгоритмов надо выдавать в школе, как и алгебру, а к универу человек уже сможет достаточно попробовать, чтобы знать, что ему интересно. И в универе да, там уже системщики получают ASM/С/С++, а прикладные программисты могут писать вообще на чем угодно.

                                                                  Хотя опять же, системщик системщиком, а я хоть и пишу в основной работе на С++, тем не менее делаю сторонние хобби-проекты на PHP/JavaScript/Python, что позволяет увидеть новые подходы, которые часто чудесно переносятся в основную работу. А пару лет назад стенфордская книжка по фукциональному программированию не сделала из меня функциональщика, но открыла глаза на то, что лямбда функции — они не только для короткой записи кастомного компаратора в std::sort(). Поэтому универ должен давать обзорные курсы разных языков и парадигм.
                                                                  • 0
                                                                    Основы нужны всем, это очевидный и объективный факт. Но возникает всё та же проблема, что именно должно входить в основы. Очевидно, алгоритмы, структуры данных, базовые понятия о парадигмах.Что ещё?

                                                                    Это та же проблема, что и с математикой. Где заканчиваются основы и начинается специализация? Производные и интегралы это основы? Пределы? Ряды? Мне повезло, в универ я пришёл из математического лицея, всё это я и так знал. А поступившие из школы не знали. Под конец обучения были те, кто без лицейской подготовки понимал непрерывную математику намного лучше. В общем, в ходе обучения границы основополагающих понятий имеют мало значения, если человек уже способен понять, что ему нужно, а что — нет. С помощью преподавателей или хороших книжек, конечно. В таком случае, основы должны быть минимальны. Шерлок не зря говорил, что «разум — это чердак». Всё туда не влезет.

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

                                                                    Что касается языка для основ, тут можно обсуждать бесконечно. Паскаль хорош для обучения императивному подходу, классика. Но после этой классики у нас только 3 из 50 въехали в функциональное и логическое программирование без особых проблем, у остальных были крайние непонятки. Для всех троих паскаль не был первым языком. Основы действтильно надо давать, начиная не с технологий и языков. Начинающим было бы намного проще понимать, если бы им показывали не «как делать это на Паскале/C++/JS» а всю концепцию сразу. Взять сразу и JS, и паскаль, и, например, Haskell. После чего объяснять основы с примерами сразу на всех трёх. Чтобы не привязывались к примитивам языка и учились понимать структуру, а не слова из кода, смолоду.
                                                                    • 0
                                                                      >Но после этой классики у нас только 3 из 50 въехали в функциональное и логическое программирование без особых проблем, у остальных были крайние непонятки. Для всех троих паскаль не был первым языком

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

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

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

                                                                        А теперь обратно к программированию. Любой школьник способен понять процедурное программирование + структуры данных. Классы — это обобщение процедур-данных. Объекты — обобщение понятия интерфейс. А функциональное программирование — это дополнительное обобщение. Упомянутая мной книжка из Стенфорда была устроена именно так: выстраивала иерархию обобщений. Ни одна документация по лямбда функциям ни в одном языке не содержала этой иерархии, именно поэтому научиться мыслить на новом уровне абстракции по ним невозможно. Если ваш университетский курс программирования и CS не был цепочкой вот таких иерархий, то меня абсолютно не удивляет, что только 3 из 50 людей въехали в теории более высоких уровней.

                                                                        С точки зрения этих иерархий паскаль как раз тот язык, который позволяет на первых порах оторваться от нюансов реализации CPU и при этом не содержит сахара, что заставляет вдумываться в написанное, и он чисто реализует самую простую процедурную парадигму, в отличие от Java, например.
                                                                        • +9
                                                                          Про «не пашут» категорически согласен. Я начал игнорировать обучение почти с первого же курса, интереснее было поизучать историю искусств, психологию, медицину, ещё много всего интересного, а математика требовала времени и вдумчивости… в общем, не совсем моё. Как и программирование, возможно, с этим ещё не решил. Но речь о другом.

                                                                          Предположим, нам нужны хорошие интерфейсы. Для этого мы, как правило, берём дизайнера и программиста. К программисту выставляем требования на знание технологий, используемых в проекте для создания интерфейсов, пусть это будет игровой проект на cocos2d-x с C++ в качестве основного языка. Если представить идеального кандидата, то получится, что для него совсем не обязательны фундаментальные знания программирования.

                                                                          Мы возьмём девушку, до этого занимающуюся веб-дизайном, потом почувствовавшую страсть к играм и изучившую cocos2d-iPhone по куче мануалов, и перешедшую на cocos2d-x по другому мануалу. Она будет хорошим кандидатом, поскольку есть опыт дизайна и интерфейсов, а плохое качество кода мы со временем устраним, с некоторой помощью старшего программиста и архитектора в одном лице. Может быть, когда-нибудь наша кандидатка займётся программированием более серьезно, но не сейчас.

                                                                          Я всё это говорю всё к тому же. Мне кажется, мы слишком высоко задрали планку «программиста», и стоит поделить некоторые вещи, а кое-что и вовсе вынести из областей, традиционно считающихся «программистскими». По моему опыту, многие учёные (в смысле, научные сотрудники), могут написать простенькую программу, облегчающую им жизнь и вычисления. Старики на фортране, молодые уже и на JS. Они же не относят себя к программистам. Для использования таких штуковин, как cocos2d-x даже не нужно особого понимания языка, только базовой концепции и инструмента. Это, условно говоря, эдакие ПТУшные специальности. Но поскольку ПТУ как класс учебных заведений для ИТ не существуют, иметь дело с этим нужно ВУЗам.

                                                                          У нас критическая нехватка кадров, а мы обсуждаем, как нам сделать идеальных программистов. Да не нужны они! Нужно решать вполне конкретные задачи. Из того гибрида мамонта с инфузорией, что выпускает большинство ВУЗов в РФ не сделать ничего. Для чисто прикладных задач они считают себя слишком крутыми, для архитекторских невероятно слабы. Есть крутые самоучки, на них ведётся охота с высокими ставками. Небольшим компаниям приходится выжимать все соки из пары гениев и молиться, чтобы те не окочурились. Не думаю, что в более «цивилизованных» странах ситуация сильно лучше. Да вы посмотрите на дырявый софт, предлагаемый «стартапами». Там площадь дыр больше, чем живого места. И это не от «продуктоориентированности», а от банальной нехватки прикладных специалистов. По интерфейсу, по сетевому взаимодействию, по базам данных… А из университетов выходят псевдоуниверсальные псевдопрограммисты, как бы знающие почти всё. Только применять не умеющие. От того следуют «трендам» не задумываясь. BigData, NoSQL, чего там ещё было за последнее время. BigData в 95% — неумение правильно структурировать. В NoSQL тоже нужно понимать задачу того или иного типа хранилища. Нужно использовать сильно связные данные — берём реляционную базу, не даром же так названа. Поименованые, но дьявольски разнородные — ключ-значение. Это можно объяснить за 2 года в ПТУ, но почему-то за 5 лет в ВУЗе — нельзя. А люди обсуждают, какой язык лучше или хуже для первого. Да не в языке дело! И король голый! На 5 человек, решающих задачи, нужен только один, досканально понимающий все эти задачи и систему в целом. И по какой-то причине у нас пытаются готовить только этого одного, но не остальных пять.

                                                                          Прошу прощения, наболело. Слова назад не возьму, всё равно карма в минусе.
                                                                          • +1
                                                                            А как это противоречит тому, что я написал? Я не считаю, что человек, не способный самостоятельно написать алгоритм сортировки массива, вообще способен адекватно кодить. А архитектору сортировки знать тоже вроде бы и не надо, но будет ли он адекватным архитектором, если не будет представлять, как работают отдельные узлы системы?

                                                                            Я за то, чтобы основы императивного подхода преподавали в школе. А дальше — веер возможностей и специализаций, выбирай не хочу. Проблема же наших ВУЗов не в широте мировоззрения (которое на самом деле необходимо, иначе уменьшается обучаемость и потенциальные способности), а в отсутствии практики. Можно сколько угодно рисовать диаграммы данных, но если не спроектировать реальный веб-магазин на курсовик, то грош цена знаниям и ноль умений. Если не преподавать курс основ безопасности — с реальной демонстрацией уязвимостей и путей решения проблем — то получите дыру вместо сайта.

                                                                            Я не понимаю, почему у нас не в почете трехлетнее бакалаврское образование. Его более чем хватает, чтобы создать узкого специалиста, который будет способен написать по проекту компонент системы. Выдали основы, выдали инструмент, выдали теоретическую базу под инструмент — вот и готовый спец. Я не уверен, что для этого хватит ПТУ, ведь даже для самой простой задачи необходимы инженерные навыки. Иначе «узкие специалисты» ПТУ-шники будут писать в индийском стиле, и попробуй их потом переучить. Могу предположить, что у вашей девушки-дизайнера все таки есть высшее образование, и для нее учиться — это нормальный процесс.

                                                                            Насчет стартапов, мне кажется, другая ситуация. У них нет времени заниматься качеством, у них деньги горят, там бы хоть что-то выкатить, а там уже посмотрим, если взлетим — то полатаем.
                                                                        • –1
                                                                          Паскаль хорош для обучения императивному подходу, классика.

                                                                          Это нормально — подоходов много, императивное используется повсюду, почему бы не начать изучение с него (это наиболее соответствует естественному восприятию алгоритма как набора шагов), а потом перейти к Object Pascal и ООП?

                                                                          Но после этой классики у нас только 3 из 50 въехали в функциональное и логическое программирование без особых проблем, у остальных были крайние непонятки

                                                                          Потому что что функциональное, что логическое программирование неестественны.
                                                                          Мы живем во вселенной, где время движется в одном направлении. Сначала я беру ручку, потом снимаю колпачок, потом пишу. Сначала я объявляю переменную, потом ее использую.
                                                                          А в логическом, мин пардон, или функциональном, нет «времени», это неестественный для человека метод. Так что ничего удивительного в том, что понимать эти 2 принципа тяжело.
                                                                  • –2
                                                                    Зная достаточно С++, вам будет легко выучить С. Изучая С перед С++ вы столкнётесь со множеством ошибок, которых легко избежать в С++, и вы будете тратить время на изучение того, как их избежать.


                                                                    Ну так в этом и есть суть обучения. Если ты умеешь работать со string, но при этом не понимаешь как строка представлена в *char, то наверное тебе работать будет не очень удобно. Большая часть задач именно по изучению С так и построены, чтобы разобрать моменты, которые надо понять, чтобы двигаться дальше.

                                                                    Порядок изучения должен быть таким:
                                                                    Assembler->C->C++->С#/Java->Методы программирования (Алгоритмика, MPI)/ Операционные системы (Kernel Drivers) -> Теория информации (Разработка в соответствии со стандартами проектирования)
                                                                    • 0
                                                                      Ваша цепочка крута, конечно… но есть ещё куда двигаться влево и вправо. :) Вообще, каждый должен выбирать, какой уровень абстракций его интересует, и сконцентрироваться на нём, заглядывая влево-вправо по мере необходимости. Весь диапазон всё равно не охватишь.
                                                                      • +9
                                                                        Порядок изучения должен быть таким:

                                                                        Далеко не всегда подход от простого к сложному работает в обучении программированию.
                                                                        Просто потому что простые решения часто трудоемки. Почему бы не начать с программирования в машинных кодах? Сразу записывая программу в шестнадцатиричном виде?

                                                                        Если простое решение настолько трудное, что человеку станет неинтересно, он просто не станет изучать дальше. Начинать стоит с чего-то среднего, спускаясь потом вниз, к ассемблеру или в другую сторону.
                                                                        • +4
                                                                          действительно, asm я бы рекомендовал системщикам-ядерщикам и (особенно) специалистам по безопасности и реверсной инженерии; и хотя благодаря микроконтроллерам asm переживает некоторый ренессанс, это уже не совсем тот asm из-за разницы в задачах. Но, опять же, куда поведёт прогресс в МК и робототехнике? Риторический вопрос.

                                                                          Лично мне тоже нравится идея обучения, разматывающая весьма непростые вещи от условной середины, по спирали, вверх, вниз и в стороны:) Тут изучают атомы и элементарные частицы, а здесь — планеты и галактики. Средняя школа и первые курсы ВУЗа должны сориентировать в цифровой вселенной, а дальше — смотря чего больше хочется в жизни: шестнадцатиричных кодов или паттернов проектирования…
                                                                        • +6
                                                                          Я когда-то начинал с C++, потом была джава, а потом и питон присоединился… Так вот я бы предпочёл чтобы всё было наоборот или хотя бы начать с джавы. Тонны ньюансов не нужны на старте. Нужно уметь программировать, а не знать, как устроена строка. вот программировать начинать гораздо приятнее на более высокоуровневых языках. А в ньюансы вдаваться по мере необходимости.
                                                                          • +1
                                                                            Пожалуй С++ после паскаля отбило у меня желание программировать на долгое время и предопределило уход в сторону от программирования в сис админы. Сейчас периодически пишу какие-то вещи для автоматизации на питоне и вполне себе доволен.
                                                                          • +3
                                                                            Почему asm первый? Может лучше сразу с машинных кодов начинать?

                                                                            Мне кажется, что надо в самое начало ставить курс алгоритмов/данных, потом С++/Java/C#, а потом уже asm (илм параллельно с С++) и потом уже ОС и теории. Фишка в том, что первый курс можно читать в школе для ознакомления с программированием вообще, а вот asm я бы в школе давать не стал, единицы поймут.
                                                                            • +3
                                                                              Почему asm первый? Может лучше сразу с машинных кодов начинать?
                                                                              С теории цепей и схемотехники.
                                                                              • +1
                                                                                Кстати говоря, у меня в институте так и было =) АСВТ было параллельно с asm.
                                                                                • +2
                                                                                  квантовая механика->материаловедение.

                                                                                  А то не все понимают как происходят квантовые процессы в транзисторах.
                                                                                  Я еше ремонтировал отдельные ячейки ОЗУ в Наири-2
                                                                                • +1
                                                                                  Может лучше сразу с машинных кодов начинать?

                                                                                  У меня именно так.

                                                                                  Мнемокоды калькулятора МК-61 -> паралельно в школе basic (казавшийся чудовищно примитивным) -> университет С++/Asm -> далее Delphi (для прикладных задач, тоже казался примитивным по сравнению с плюсами) -> C# и опять С++, уже обновленный
                                                                                  • 0
                                                                                    У нас в школе были калькуляторы с мнемокодами, но уже тогда я знал паскаль и имел представление про asm, поэтому калькуляторы казались чем-то излишним.

                                                                                    Интересно, чем вам примитивным казался Object Pascal? Я помню, меня приводило в полный восторг, как Borland удалось сделать из довольно неуклюжей объектной модели TP весьма изящную на то время VCL, по образу и подобию которой потом проектировали .NET. С++ конечно дает более легкие средства контроля над выполнением кода, но в плане создания интерфейсов он все же попроще.
                                                                                    • 0
                                                                                      OP казался примитивным из-за отсутствия (тогда) шаблонов и обобщений (generic).
                                                                                      Хотя рисовать Win программы было очень легко и удобно.
                                                                                  • 0
                                                                                    Асм это мнемоническое представление машинных кодов. Разница лишь в удобстве написания и чтения.
                                                                                    Когда я начинал кодить, я делал это в маш.кодах, т.к. ассемблера не было.
                                                                                    • 0
                                                                                      Это фатальная разница, тем не менее.
                                                                                • +4
                                                                                  И, наконец, какая из версий compose() более эффективная? С++ — потому что ей не надо подсчитывать символы в аргументах и она не использует динамическую память для коротких строк.
                                                                                  Однако Великий слегка гонит — хотя оптимизация аллокаций для коротких строк за счет встроенного в std::string буфера возможна, никто не гарантирует что так и будет. Например в libstdc++ и libc++ это не так (стандартная C++ либа на Linux и новая либа от создателей Clang). Если скорость некоторого куска кода весьма важна, приходится разбираться и лезть в кишки.

                                                                                  С точки зрения самого языка все ОК — ничего не мешает написать контейнер с нужными свойствами. Но ожидать что STL прямо так оптимален для всего наивно. Сложно удовлетворить конфликтующие требования, например оптимизация для коротких строк увеличивает размер объекта и на некоторых задачах это может быть нежелательно.
                                                                                  • +1
                                                                                    В libc++ SSO есть. В libstdc++ будет наконец в следующей версии (если все пойдет по плану).
                                                                                  • +12
                                                                                    > 2. Миф 1: …

                                                                                    Никогда не делайте так заголовки!
                                                                                    • +6
                                                                                      А все проблемы отчего? Оттого что индексация не с нуля!
                                                                                      • 0
                                                                                        1. Миф 0.
                                                                                        Так лучше что ли?
                                                                                        • 0
                                                                                          0. Введение
                                                                                          1. Миф 1

                                                                                          i. Миф i

                                                                                          N. Миф N

                                                                                          Индексы — служебная информация, названия — пользовательские строки, поэтому индексация разная.
                                                                                          • +1
                                                                                            Вся штука в том, что нумерация заголовков не нужна тут вообще.
                                                                                    • 0
                                                                                      Ну преподавать СРАЗУ С++ я считаю слишком для юной психики. Миф о том что С++ — это хороший язык чтобы понять основы — не более чем миф.
                                                                                      Начинать надо с простого языка, чтобы преподавать основы алгоритмики: Python для начала хорош, C# если к типам хочется приучать сразу, Pascal старый добрый тоже хорош, но на нём просто уже не пишут.
                                                                                      C++ и Java всё же два языка, до которых надо дорасти, особенно C++. Как правило к языку C++ и идут через язык C, потому что язык C позволяет понять работу с памятью и адресацией, чтобы потом перейти к Assembly, а затем также пойти вверх и выучить ООП и шаблоны языка C++.
                                                                                      И да, инициализация вектора как массива в C++ работает далеко не во всех компиляторах, которые заявляют о поддержке C++11.
                                                                                      Слишком оптимистичная статья в отношении C++. Могу пройти по всем мифам, но в целом всё верно, за исключением тона подачи материала.
                                                                                      • +1
                                                                                        Очень сильно зависит от самого обучающегося :) Я в свое время еще в средней школе начал изучать программирование с C++ и до сих пор считаю это правильным подходом.
                                                                                        • 0
                                                                                          Java — очень простой язык с очень сложной экосистемой. Я бы как раз начинал учить народ именно Java, только заменив JDK чем-то менее разухабистым. Ну или заставляя студентов реализовывать большинство применяемых классов.
                                                                                        • +6
                                                                                          У Страуструпа два достоинства — он сделал язык в свое время и в своем месте, нужный сообществу и безальтернативный в своей нише до сих пор, и не забросил его, как Вирт забрасывал кучу своих языков. Но его статьи и книги… Лучшая книжка Страуструпа — Дизайн и эволюция языка С++. Иными словами «список костылей для С в хронологическом порядке». И С++, по-моему, учить нужно так: Язык С Кернигана и Ричи, затем Дизайн и эволюцию, затем уже книги Саттера или Мейерса для best practices, затем их статьи для С++11. Если ничем другим не заниматься, прочитать эти 3000 страниц можно за неделю. А дальше, зная всю хронологию и где что смотреть, спокойно брать любой учебник по С++ и решать задачки, процент wtf будет минимальным.
                                                                                          А вот книжки чисто по С++ у Страуструпа какие-то странные. Видно, что за образец он взял Кернигана и Ричи, но обучать людей С++ таким образом, как будто всей его истории не было — по-моему заставить человека говорить wtf на каждой странице и ничего не понять в итоге.
                                                                                          • +1
                                                                                            Частично wtf снимаются повторным прочтением книги. Сложные технические книги лучше вообще читать дважды (если они интересные при этом, конечно): так из них усвоится почти ровно в 2 раза больше информации, проверено.
                                                                                          • +3
                                                                                            Насчёт ругани на Java по поводу отсутствия автоматического управления ресурсами — а чем try-with-resources не угодил? Очень просто, удобно и понятно, сразу видно, где конкретно ресурс будет закрыт. Можно, конечно, забыть его написать, но и в C++ масса способов забыть удалить память/закрыть ресурс.
                                                                                            • 0
                                                                                              Насчёт ругани на C по поводу отсутствия сборщика мусора — а чем аллокация на стеке не угодила? Очень просто, удобно и понятно, сразу видно, где конкретно память будет освобождена.
                                                                                              • 0
                                                                                                Ну, справедливости ради, из-за инлайнинга это может быть уже не так очевидно, хотя это скорее ошибка компилятора, чем способа. Почитайте о проблемах использования alloca (выделение памяти на стеке).
                                                                                                • 0
                                                                                                  Я не об alloca, а старом добром выделении структур на стеке (типа `struct Foo foo;`). Штука удобная, но крайне ограниченная — указатель на такую структуру можно передавать только вниз по стеку, причем следя за тем, чтобы нижележащие функции тоже никуда этот указатель не передали. Ну тут все понятно. Да здравствует сборка мусора, где об этом думать не нужно.
                                                                                                  Но да здравствует ли? Страуструп говорит, что нет. Память — всего лишь один из видов ресурсов. И try/using является _точным_аналогом_автоматической_памяти_ в мире ресурсов. Он примитивен. Try/using — это чисто ограничение времени жизни ресурса фигурными скобками. Если же время жизни ресурса нужно отвязать от фигурных скобок (например, заныкать в объект со счетчиком ссылок на этот ресурс, завести пул ресурсов и т. п.), начинаются те же самые проблемы, которые есть с парами malloc/free в языке С. На сборщик мусора полагаться при этом нельзя, потому что он только для памяти и вызов финализатора недетерменирован, финализация вообще может никогда не произойти. В итоге и получается, что код управления ресурсами на языке без RAII похож на код на C с ручными free (явный подсчет ссылок на ресурс и вызов финализатора) или же приходится писать суррогатный RAII с батареями try, где ссылки на ресурс будут считаться автоматически.
                                                                                              • +1
                                                                                                Не знаток Java, но судя по описанию есть ещё одна проблема, кроме как не забыть везде писать try для подобных объектов. Пусть несколько членов-данных какого-то объекта реализуют этот метод close(), тогда внутри метода close() такого объекта мы должны будем явно вызвать руками все эти методы, тогда как для деструкторов в С++ соответствующий код будет сгенерирован автоматически.
                                                                                                • 0
                                                                                                  Есть такая особенность, да. Когда вы пишете явно close для своих ресурсов, вы указываете, что являетесь их владельцем, и жизненный цикл ресурса совпадает с жизненным циклом вашего объекта. В плюсах вы должны взамен аккуратно использовать unique_ptr или shared_ptr.

                                                                                                  Кстати, в Java неплохо решили проблему выбрасывания исключений из close(). В плюсах, насколько я понимаю, обработать исключительную ситуацию при закрытии ресурса не представляется возможным.
                                                                                                  • +2
                                                                                                    В плюсах вы должны взамен аккуратно использовать unique_ptr или shared_ptr.
                                                                                                    Ну уж нет, аналог вашей конструкции в плюсах — это простое создание объекта на стеке, деструктор будет вызван автоматически при выходе из области видимости.
                                                                                                    В плюсах, насколько я понимаю, обработать исключительную ситуацию при закрытии ресурса не представляется возможным.
                                                                                                    Вы можете бросать исключения из деструкторов, тонкий момент тут только один: если в процессе раскрутки стека при обработке исключения из деструктора разрушаемого объекта бросается ещё одно исключение, то работа программы прекращается (вызывается std::terminate()), что в целом довольно логично. Сделать как в Java невозможно хотя бы по той причине, что C++ не требует, чтобы объекты-исключения наследовали какой-то интерфейс, вы можете бросить всё что угодно, например, обычное число. C другой стороны у нас есть функция проверки не идет ли в данный момент процесс раскрутки стека (std::uncaught_exception()), поэтому с известными ограничениями можно попробовать реализовать подобный механизм самостоятельно.

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