Pull to refresh

Comments 174

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

Ну значит что Хирург — человек который режет за ровно отведенный срок, нужный кусок мяса, и надежно зашивает дырку после этого! А иначе он мясник, любитель порезвиться скальпелем, для него это просто хобби, или он любит людей!
Жестоко, но в этом есть смысл, если хирург в срок не уложится, то пациент станет куском мяса.
А с Хирургом, кстати, очень хорошая аналогия! Btw, как вы думаете, почему выпускников медвузов, таких «начитанных» и «умных» (свеже помнящих наизусть все «умные книжки» по медицинской теории, со всей еще умной латинской терминологией) не пускают сразу сразу «резать людей»? Как вы думаете, почему они сначала только «ассистируют» (т.е. внимательно смотрят за «реальной практикой» хирурга только «инструменты подают»), а «скальпель в руки» им дают только тогда, когда появляется хоть некоторая «призрачная» гарантия, что молодой хирург не начнет буздумно пытаться «притянуть за уши» живую жизнь на субьективно понятый «догматизм теорий» вычитанных в книжках? Иногда мне кажется, что подобного подхода весьма не хватает в индустрии «software development» особенно в создании тех приложений, от которых зависит жизнь и здоровье.
Они не только ассистируют, они ещё часы практики на трупах набирают…
Каждый дизайнер должен поработать сустейнером (с)
В последнем предложении можете смело выбросить слова «Иногда мне кажется, что» — так и есть к великому сожалению. По поводу чего в душе моей перманентно поселился перманентный double facepalm…
«В следующий раз не нажимайте так сильно на скальпель. Столько столов уже испортили.» (с)
Когда зубная врачиха в 1992 году вырезала у меня из губы слюнную железу, она позвала свою коллегу посмотреть. Наверное, случай сильно напомнил что-то «класическое». Я же сидел возмущённый тем, что мне не показывают;-). Хотя, наверное, показать именно мне было бы проблематично…
Ну, у хирурга работа такая, что ни затягивать ни спешить смысла нет. Думаю, нормативы времени на операции есть, но вероятность нештатного хода операции тоже ненулевая.
Tip: я бы не стал развеивать «заблуждение», опираясь всего на одну статью одного человека — возможно, в комментариях ниже будут аргументы/пруфы, почему «заблуждение» и не заблуждение вовсе. Как-то так.
Всегда пожалуйста. Оставайтесь на связи. Продолжение следует.
Вы уж простите, но это убило
image

а по теме, спасибо, я жду продолжения но, большинство книг по С++ абсолютно не объясняют саму суть ООП, и приводится мало параллелей с реальным миром для более просто понимания. А если неподготовленного человека сразу бросать в
class App  
{
public:
    int Run()  
    {  
        std::cout << "Здраствуй Мир!!!\n";  
        throw std::runtime_error("Что-то плохое в нашей программе случилось.");  
        return 0;  
    }  
    ~App()  
    {  
        std::cout << "До свиданья.\n";  
    }  
};


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

Он просто напросто захлебнётся.
К сожалению мне вашу картинку не видно.

Думаете, ему сразу перестанет быть интересно?
Лично мне кажется, что материала, разжевывающего все до самых мелочей вокруг навалом, достаточно воспользоваться поиском. А вот выбрать направление для этого поиска — это большая проблема.
>>абсолютно не объясняют саму суть ООП
Сначала Вы почитаете умную книгу. Придет понимание что Вы уже знаете ООП. Потом столкнетесь со множеством наюнсов в разных программах и проектах. Придет понимание, что Вы ничего не знаете про ООП. Пройдет время и Вы сами выведете для себя что же такое ООП для Вас лично. А потом придет время и попадется книга, книга которая покажет что Ваше мнение целиком и полностью совпадает с тем что в ней написано и вы воскликните «Вот ведь! А где же ты золотая раньше была?» и именно эту книгу вы будите рекламировать любому, кто решил узнать про ООП. А этот «любой» пройдет тот же самый путь к пониманию ООП, который прошли Вы и не факт что «его золотая книга» == «Ваша золотая книга».
Вы совершенно правы, коллега :-)
Прочитал статью трижды, так и не понял на кого она ориентирована. Подскажите вашу целевую аудиторию?
Те, кто уже знаком с языком, но еще не знаком с процессом создания программ на промышленном уровне.
Это «введение» из ожидаемой серии постов.
Да, но, правда тем программистам, которым реально предстоит «знакомство с процессом создания программ на промышленном уровне» подобная серия статей будет по меньшей мере бесполезной, если не сказать даже вредной. Идеологический «догматизм» в неокрепших умах «не нюхавших пороху», знаете ли, может быть даже более губительным чем их чрезмерное «увлечение модой».

После ваших постов ожидаю страшную армию говно-кодеров.
Не пишите больше ничего…
Те, кто только начинают, не поймут, зачем это нужно.
Те, кто уже просек фишку, и так это знают.
Это можно повторять самому себе для закрепления.
Но форма изложения ведет к пустому, если не отрицательному результату.
Мне кажется, что статья похожа на «крик души». Человек очень долго работает с С++ и С и ему часто встречается неправильное использование языков и технологий. Естественно, его как адепта и хранителя правильных традиций это коробит и вызывает раздражение. Это все копилось и копилось. И вот одно из проявлений накопленного недовольства — эта статья.
Мне тоже кажеться, что нужно было кроме собственно своего крика вложить еще и стержень в эту статью и разобрать хотя-бы несколько шаблонов, сравнить типовые реализации их на С и на С++ и этим самым показать разницу идеологий этих языков.
Вероятно это начало цикла в котором все это будет, так как в рамках одного поста это все сделать будет слишком объемно.
Часто — это по моему даже слишком мягко сказанно.
Неправильное использование технологий — это «бич» нашего времени, когда код пишут люди различного уровня подготовленности. И часто нет правильных наставников, которые бы их били по рукам и говорили как нужно. Вот и результат и к такому положению вещей нужно относиться философски :-)
Поэтому имеет смысл адаптировать технологии таким образом, чтобы написать неправильно было сложнее, чем написать правильно :-)
Поэтому сейчас начинают входить в моду множество языков программирования, в том числе и DSL.
Выбешивают особенности виновника торжества (С++), такие как возможность написать конструктор копирования с передачей копии объекта, а не по ссылке, или базовый класс с невиртуальным деструктором,. Как мне кажется такие вещи не должны компилиться, а падать с ошибкой, ворнинга недостаточно. Но нет даже ворнинга. Это за пределами возможности выстрелить себе в ногу, это как документированная возможность вида «нажмешь эту кнопку — умрешь». Зачем кому либо вообще такая кнопка? Почему нельзя было сделать без нее?
Не знаю какой долб**б вас минуснул. Согласен с вами на все сто…
(а на хабре кстати можно писать долб**б без звездочек?)

А меня выбешивают операторы, а точнее их отсутствие — операторы в С++ это тупое наследие С — какой-то набор продиктованный ассемблером… непонятно… что-то расширяют, но остаются на том же месте… куча непонятных ограничений… ну вот почему есть возможность в С++11 делать суффикс для чисел и обрабатывать их во время компиляции, а для строк нельзя…
Короче, подход, абсолютно не системный… Дискредитирует (С++) себя на каждом шагу… я только теперь начал понимать почему некоторые уважают С но терпеть ненавидят С++…

Ничего=) прорвемся!!!
Не нашел там конструктора копирования, там речь про operator=, разве нет?

Кстати проверил в Visual C++. Не дает создать конструктор копирования с аргументом по значению. Не знаю, заслуга ли это микрософта, или 11го стандарта, но претензия была напрасна. Про невиртуальный деструктор, тем не менее, «особенность» здравствует и поныне.
Да, действительно, не про конструктор копирования, разучился читать, простите =)
Целивая аудитория на фото в начале статьи!
Вряд ли Беар будет читать что-то про С++ ))) Тут же не написано, как выживать в дебрях говнокода.
В конце посмеялся от души:
  help: Command not found.
  % damn!
  !: Event unrecognized
  % logout

:-))
да ладно, там в классе string конструктор приватный, а объект создается)
UFO just landed and posted this here
А зачем на C++ писать не «аля ООП», когда есть другие языки? Тот-же C например.
> C++ в первую очередь мультипарадигмальный язык программирования.
Полностью поддерживаю про мультипарадигменность. Использование компилятора С++, может быть обусловленно массой причин. А писать на С++ слава богу можно и в функциональном стиле если душа/подход/задача требует. Мне кажется, что догма не есть хорошо. На мой взгляд, хорошая программа должна не только выполнять задачу, но и как можно яснее выражать мысль. Если это сделано в рамка С с классами, хорошо выпоняет задачу и понятно тем кто пришел поддерживать после автора честь и хвала автору. А те примеры что я вижу в статье, прошу прощения никуда не годятся и ничего полезного не демонстрируют. Какие-то выкрученные руки и ноги.
Функция main для нас неизбежное наследие С и избавиться от нее без лишних проблем тяжело. — очень интересно, как бы вы от нее избавились.
Прочитать описание таких вещей, как Паттерны.- если для новичков, то зачем им паттерны. У нас был один начитавшийся, который пытался их суньть везде, где нужно и не нужно.
Советую автору почитать мнения Дейкстры о ООП.
Все задачи в заданные сроки пишутся — вы Blizzard об этом скажите.
main можно переименовать. Можно ее запихать внутрь класса. Можно еще много чего с нею сделать. Вот только смысла от всех этих извращений абсолютно никакого.
Покажите пожалуйста, как «запихать» main внутрь класса.
Можно как в java. А можно так:
class Main { void main(); };
и обязать рантайм создавать экземпляр класса Main и вызывать у него функцию-член main(). Но смысла в этом никакого потому что ООП — это не «больше никогда не пишем non-member functions», да и C++ — не чистый ООП язык.
Вы не поверите, но это корректный код на C++ — почитайте стандарт. Если у вас это вызывает непонимание или смех, это ещё раз доказывает, что писать пытаться быть наставником для других, как в этой статье, — не ваш уровень.
Она от этого не пропадет. Компилятор должен знать, куда в бинарнике должен происходить переход после инициализации программы.
Читал популярную книжку по паттернам. Вынес для себя две полезные вещи: 1) разжевали на пальцах, что такое Java RMI и с чем его едят 2) ещё одно подтверждение, что писать код медленно — это не сильно плохо.

Про сами паттерны лучше всего сказал Paul Graham: I wonder if these patterns are not sometimes evidence of case (c), the human compiler, at work..
Ну это ж не избавление от main. Выкидывается предварительная инициализация. И в обоих случаях, рано или поздно, но мы попадаем в наш main.
>>Ну это ж не избавление от main.
Почему это нет?

из Ваших:
>>Выкидывается предварительная инициализация.
Вот как раз из этой «предварительной» и происходит вызов нашей привычной и родной нам main(), которую мы пишем не задумываясь о кухне на начальном этапе. Если этот «предварительный код» выкинуть из /entry и написать свой костыль, то не кому будет позвать main().
Обзывать main можно как угодно, но линкеру все равно нужно знать точку входа в программе. Я сейчас об этом.
C не язык типа Prolog'а, где на каждой «итерации» ищется наиболее подходящее правило для выполнения. И либо вызывается скрытый от нас startup код, который потом переходит к нашей main (ну или как мы ее заходим назвать), либо сразу наш явный код.
>>Обзывать main можно как угодно
Вы не в ту степь! Рекомендую сделать паузу.
Мои слова про main были написаны после того как DeusModus написал слова: «Функция main для нас неизбежное наследие С» имея ввиду не AddressOfEntryPoint в PE-файле или аналог в Elf,MachO файлах, а ту самую «пользовательскую» main() которая зовется из CRT.

Он даже написал:
«и избавиться от нее без лишних проблем тяжело»

Еще раз: речь идет не о той точке входа которая указывается в заголовке исполнимого файла, а о функции которая зовется из CRT! Именно об этой «пользовательской» функции написал изначально DeusModus и именно на его слова написан мой ответ. То о чем вы говорите «можно как угодно» это относится к точке входа указываемого в заголовке исполнимого файла.
Да, мы о разных вещах говорили.
Тогда Вам вопрос: Я правильно помню что временные метки в PE файлах отсчитываются в секундах от «16:00 31 декабря 1969»?
Да, но к чему тут этот вопрос?
Не в этой теме, если интересно то в личку )
пока есть int main(void) вы тоже программируете на «Ц с классами» ;)
Ну, по крайней мери в малой части своего кода.
а вообще, вы бы уже пофиксили компилятор, чтоб избавиться от main и достичь полной нирваны ))
Я очень уважительно к нему отношусь и ни разу его не оскорбил.
Мне не нравиться смесь этих языков. «Ц с классами» — не делимое словосочетание.
In computing, C (/ˈsiː/, like the letter C) is a general-purpose programming language...[1]
Ну можно было написать «Си с классами», если уже так хотелось на русском.
А то прямо коробило видеть эту «Ц».
Вообще первая версия плюсов, которую писал Строуструп, так и называлась «C with Classes». Только в 1983-м переименовали в C++. Потому выражение «Си с классами» я бы скорее воспринял как отсылку к той ранней версии (которая была больше препроцессором, чем полноценным компилятором). Но «Ц с классами» мне тоже не нравится, впрочем, как и вся статья :-)
Ну да, первая версия и была препроцессором.
Поосто обычно название ЯП оставляют как есть, а не транслитерируют.
Функция main для нас неизбежное наследие С и избавиться от нее без лишних проблем тяжело.

Зачем избавляться от функции main() и что «правильное» хотелось бы видеть вместо нее?
Например, у функции main нету деструктора, который был-бы вызван при размотке стека при обработке исключения.
Откуда у *функции* деструктор? Вы вообще на C++ когда-нибудь программировали?
Скорее, не «откуда» (ниоткуда, его нет), а «зачем». У локальных переменных в функции деструкторы есть.
> Например, у функции main нету деструктора

Автор написал так, как будто у всех функций деструктор есть, а main какая-то неполноценная.
Не у всех. Но у некоторых он есть.

Вот только речь действительно шла о том, чем-же всетаки плоха конкретно main. Мне в ней не хватает деструктора и try cathc по дефолту.
> Не у всех. Но у некоторых он есть.

Очень интересно. У каких функций, по вашему мнению, есть деструктор и что он делает?
У тех, которые являются функторами.
Стандарт C++ не описывает термин «функтор». Тем не менее есть термин «function object», который говорит сам за себя — это *объект*, а не функция.
С функторами в частности и с вызовом member-функций вобще есть такая неприятность — нет никаких гарантий, что объект не прекратит свое существование во время выполнения функции. Оно и понятно — память то управляется вручную, а сделать «this» умным указателем невозможно, так как он (неявный дополнительный параметр) генерируется и передается автоматически.

Типичный пример отсутствия целостности в дизайне языка C++. О какой «чистоте» тут может идти речь?

Я не хочу сказать, что C++ не пригоден для решения практических задач — еще как пригоден, и иногда это лучший инструмент из доступного. Но «чистотой» дизайн этого языка не отличается, это факт.
> нет никаких гарантий, что объект не прекратит свое существование во время выполнения функции

Да и вообще любой object может быть освобождён, причём любым потоком! И любой другой поток может создать вашему потоку data race! И совсем никаких гарантий!
Так нечестно) Если потоков больше одного, то состояние мира перестает быть детерменированным, и все держится на соблюдении conventions и policies. Если нужны гарантии в части многопоточности, придется сменить язык.
Хочу еще раз аккуратно изложить свою мысль.

Пример с многопоточностью неправильный. Это и многое другое — следствие того что язык низкоуровневый «как Си, только лучше». Объект превращается в тыкву, если затереть пару байт в начале, а приватные поля можно прочитать с помощью memcpy. Это не недостатки, это особенности.

С другой стороны, умные указатели vs. this это конкретная такая недоработки в дизайне языка. Потому что сначала нам дают инструмент, с помощью которого можно управлять памятью более безопасно («умные указатели»), а потом оказывается, что этот инструмент неполноценный, потому что при вызове member-функции его задействовать нельзя.
Если в вас возникает такой ужас при виде this, можно наследоваться от enable_shared_from_this и обращаться к объекту через shared_from_this(), например.

А гарантия того, что функтор не исчезнет во время исполнения — это ваша задача: lifetime, ownership и прочее.
Наследование от enable_shared_from_this фактически добавляет weak_ptr на себя отдельным полем; это не помешает объекту удалиться. Кроме того, для вызова shared_from_this необходим валидный this; так что ваше решение от «протухания» this не спасает.
Нет, это решение не против протухания, а чтобы даже this был smart_ptr-ом. Для использования shared_from_this() необходим владелец, и я точно скажу, что это замечательно, когда все heap-allocated объекты встроены в иерархическую структуру владения.
Пожалуйста поподробнее, каким образом это сделает this умным указателем? Мысль про владельца я не понял. AFAIK для того, чтобы shared_from_this работал правильно достаточно создавать объект через make_shared, при этом происходит инициализация weak_ptr на себя, который хранится в объекте. Делается это с помощью вызова sp_enable_shared_from_this.
Ну, вместо того, чтобы обращаться через this->something, вы будете делать что-нибудь типа shared_ptr smart_this(shared_from_this()); smart_this->something. В этом предложении вообще была доля сарказма изначально, если что.

Чтобы работал shared_from_this, у объекта должна быть хотя бы одна не-weak ссылка — то есть, объектом кто-то должен владеть через shared_ptr. Поэтому просто создавать объект через make_shared недостаточно (и, в общем-то, необязательно), нужно где-то держать на него живой shared_ptr.
Вместо
this->something

предлагаете
smart_this(this->shared_from_this()); 
/*         ^^^^^^    */
smart_this->something

Зачет)

Чтобы работал shared_from_this, у объекта должна быть хотя бы одна не-weak ссылка — то есть, объектом кто-то должен владеть через shared_ptr.

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

Кстати, что мы подразумеваем под тем что shared_from_this работает? Метод вернул nullptr потому что на объект не осталось ссылок, это вполне корректная работа с точки зрения спецификации.

С другой стороны, если weak_ptr на себя не был проинициализирован, а в конструкторе базового класса это сделать не возможно, то shared_from_this работать не будет. В make_shared для инициализации зовется sp_enable_shared_from_this:
template<class T, class Y> void sp_enable_shared_from_this( shared_count const & pn, boost::enable_shared_from_this<T> const * pe, Y const * px )
{
    if(pe != 0)
        pe->_internal_weak_this._internal_assign( const_cast<Y*>(px), pn);
}


Что-то мне кажется, что если создать объект с помощью new, поле _internal_weak_this не будет проинициализировано:
boost::smart_ptr<T> smart_p(new T); 
Я же не пишу, что придумал способ избавиться от this, я пишу о том, что его можно обернуть в smart_ptr и пользоваться так, если «в вас возникает такой ужас при виде this». Это такое саркастическое предложение, понимаете?

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

class Type:
  public enable_shared_from_this<Type>
{ };

unique_ptr<Type> object(new Type());


То в этом случае нет вообще никакого смысла использовать shared_from_this — он никогда не будет работать.

Вы ошибаетесь в том, что shared_from_this может вернуть nullptr: он либо вернёт shared_ptr на живой объект, либо бросит исключение.

А ещё вам неправильно кажется: на самом деле, weak_ptr будет проинициализирован и в случае создания shared_ptr через конструктор с указателем.
OK, посыпаю голову пеплом по обоим пунктам.
Раскройте мысль подробнее, пожалуйста.

Почему у вас объекты помирают во время работы с ними, и причём здесь функторы?
Именно function objects тут особо не при чём.

#include <vector>

struct A;

std::vector<A> As;

struct A {
  void f() {
    As.clear();
  }
};

int main() {
  As.push_back(A());
  As[0].f();
}
Автор утверждает, что функторы лучше обычных функций, потому что у них есть деструктор. «Контр-аргумент»: нет, функция все же лучше, потому что объект может сдохнуть (теоретически) до завершения вызова, а с обычной функцией такая неприятность нам не грозит.

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

А чем это может помешать голому объекту-фугктору?
Либо это расхождение терминологии и понятий, либо вы не про C++.
Возможно, он имел в виду что у main деструктор виртуальный и его не так хорошо заметно
Наверно что-то типа atexit() хочет, но в C++ — style.
Ну да, с конструктором #pragma startup function-name
> Дело в том, что class — вовсе не означает объект из ООП, как обычно пишут в толстых и умных книжках.

Это в каких же книжках так написано? Класс — это класс, а объект — это объект. В любом объектно-ориентированном языке.
может решить поставленную алгоритмическую задачу в заявленный срок с заданным уровнем качества.
Забыли добавить «выберите любые два пункта из этих трёх».
Начать, что ли, эпический холивар?
Автор, Вы странно понимаете С++. Да, я видел слова «20 лет» — ну и что?

1. С++, как уже было сказано выше, мультипарадигменный язык. Не всё в нём классы. Всё классы в Яве какой-нибудь. Это важная часть языка, это мощная штука, но это всё-равно что сказать «в русском алфавите самая главная буква — О!»
2. «Ц с классами» — слабый риторический приём. «Сиплюсплюс» — тоже мерзко выглядит, правда?
3. Ваш пример превращения «Hello world» в «правильный С++» просто ужас. Он ничего не упрощает, он создаёт новые проблемы и усложняет код. Хотите рассказать о том, как пишется «сложный enterprise код» — найдите адекватный пример, а не этот кошмар.

Ну, а предлагать аудитории Хабра почитать о паттернах (при том что тут уже были сотни статей о паттернах и десятки статей подобного вида) — дык вообще моветон.
Что за мода такая пошла, «воткнуть» абы что в качестве сопровождающей фотографии? Вот объясните мне «Я — Серега. » чем вы руководствовались?
Быть может, что автор как бы проводит параллель между собой и Гриллсом в разрезе крутости :)
Открою секрет. Это не автор выбрал картинку, а я. И руководствовалась исключительно грязью на лице актера.Т.е., исходя из содержания статьи, а главное, из ожидаемого тона комментариев, нужно было что-то грязное. А так как что-то неприличное исключается, а некрасивое — не приветствуется, то вариантов осталось не так много. Вот и.
Автор, какие ещё языки программирования вы знаете достаточно хорошо (т.е. программировали на них более года минимум, и код был сложнее чего-то, что на одном-двух экранах помещается)?

Мне просто кажется (видя тот код, что вы здесь приводите), что это уже профессиональная деформация какая-то — на пустом месте нагородить столько кода, что уже не видно, что же этот код делать-то должен.
Такое впечатление, что увидел несколько отрывков из умных книг, сложенных в один тазик. И в большинстве этих книг авторы видят С++ по разному. И каждый опытный программист видит этот язык по своему. Вот вы его видите так. А я немного иначе.
За статью +17, а в карму автору сильно наминусовали. Хабр такой хабр.
А потому что буквы в слова складываются верно, и даже код из примеров компилируется, а вот стиль «Один я знаю, как надо правильно программировать, а вы пока почитайте про паттерны!» вызывает такие вот эффекты.
Это само собой. Вопрос в другом, почему топик плюсы собирает? Лично я в нем не нашел ничего интересного, кроме украинского слова «ихний» (от "їхній") имеющего то же значение.

Считаю необъективным минусовать/плюсовать карму за отдельный топик. Просто те кто минусуют, в отместку за потраченное время наследят и там и там.
Рискну предположить, что топик собирает плюсы, например, от тех, кто сам в топик не вчитывался, но кому понравилась брутальная фотка Беара Гриллса (ведь, согласитесь, Беар Грилс — няшечка, а фотка для привлечения внимания — это половина успеха статьи, маркетинг, сударь ) :) А минусы самому автору, уже подозреваю, от тех, кто статью-таки прочитал, и кому: 1) просто оказалось совсем не по нраву «разжигание холиваров», 2) а также от тех, кто считает «необоснованно предвзятым» отношение автора «я — Серега — компутер видел еще 20 лет назад, когда вы пешком под стол ходили и теперь я крут как Беар Грилс, ведь я работаю в Интел и знаю ООП, а вы, сынки, идите учите C++ и Паттерны». Кому-то, конечно на «эти Д'Артаньянские понты» (в виде прозрачно читающегося намека) пофиг, а кого-то ведь это реально задевает, так что ничего удивительного.

void foo();

foo();
Это самый что ни на есть C.

Это самый что ни на есть C++.
Дальше читать не стал.
Для тех кто не в курсе:
void foo(void);
Вот это C.
Он под void foo() имеет в виду void foo(...) в старом написании семидесятых, если копаться.
В C++ все есть класс.


Откуда такая информация?

int a = 1; // 'a' - не класс.
void foo() {} // 'foo' - не класс.


Возьмем например вашу первую программу на «Ц с классами»
#include <iostream>  
int main(void)  
{  
    std::cout << "Здраствуй Мир!!!\n";  
    std::cout << "До свиданья\n";  
    return 0;  
}


Откуда русский текст в ANSI-литералах? Это что за стандарт C++ такой?
Неплохо бы сразу обозначить, для кого предназначена статья :)
Тяжело нынче живется пуристам, Такое обилие технологий и парадигм вокруг.
А мне понравилось. Суровая статья о суровой правде программерской жизни. Иногда сам себя ловлю на таком же кривом «Ц с классами».
Иногда Ц с классами выглядят куда красивее этого ада, который заместо них предлагают пуристы от ООП.
KISS всё-таки никто не отменял.
На счет пуризма — все относительно. Когда-то я по молодости тоже считал что С и С++ это почти один и тот же язык. И мне показывали неоднократно, почему я заблуждаюсь и в чем разница между языками и их применением. И соответственно, между подходами. А вообще, это забавный холивар.
Началось…
На самом деле весьма занимательно, зная синтаксис вполне себе легко читается.
Честно скажу автору — спасибо! Такие как я — ваша аудитория.
Знаю синтаксис, много программ для себя, а как писать правильно так и не ясно, с нетерпением жду продолжения!
Цикл с легкостью можно назвать «Философия программирования С++» он, как мне кажется, будет объективно раскрывать суть проблемы.
В статье даны примеры, как надо делать, но ничего не сказано, в каких случаях это применимо, а в каких нет. Постулируется, что предлагаемые решения всем хороши, но не доказывается это.
Вот была простая функция
void foo();  

Но её зачем-то заменили на:
class Foo  
{  
public:  
    static void foo();  
};  
...  
Foo::foo(); 

Зачем? Она приобрела от этого какие-то новые свойства? Код стал более читаемым? Код стал короче? Нет, для компилятра это такая же свободная функция, только помещенная в пространство имен Foo. Причем в этом пространстве имен всего одно имя. Я пока нигде в литературе не встречал термин «простнаство имени», а namespace'ы обычно применяются для группировки семантически связных объектов. Так что имеем просто удлинение имени функции.
Однако, если в namespace поместить несколько функций или классов, смысл появляется.
Другой вариант:
class Foo  
{  
public:  
    void operator()();  
};  
...  
Foo foo;  
foo();

У функции появилось состояние? До этого я знал, что вызвав её с какими-то аргументами я получу один и тот же результат (будем считать, что функции с побочным эффектом имеют имена, явно об этом говорящие), теперь это не очевидно. Более того, я знаю, что вызов функции потокобезопасен (я все еще надеюсь, что функция, оперирующая глобальным состоянием будет иметь соответсвующий комментарий), а функтор, у которого operator() не объявлен как const почти наверняка не является потокобезопасным.
Ваш код обманывает меня.
class IFoo  
{  
public:  
    virtual void foo() = 0;  
}; 

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

Про main. Что вы выиграли, создав класс вместо функции? Того же самого результата можно добиться используя scoped объекты для хранения ресурсов. Только scoped объект очищает свои ресурсы, а что будет освобождать деструктор предлагаемого вами класса App? О каких ресурсах приложения не позаботится компилятор?
Советую автору изучить такой язык как Java.
Так-же не плохо было бы написать несколько сотен тысяч строк на Pure C.

После этого для С++ не останется места. вы познаете главную истину программирования:

Все что можно написать на Java — нужно писать на Java.
Для остального есть Pure C.

Мне жаль тех людей, кто до сих пор занимается костылеписательством на С++ :-(
Как-то бредоподобно звучит «Все что можно написать на Java — нужно писать на Java.». Вообще Java (как и C++, и даже Brainfuck) — Тьюринг-полный язык, так что любую вычислимую функцию можно написать на Джаве. Но можно и на Brainfuck. Почему бы не писать абсолютно все на Brainfuck? И ответ простой, — потому что на Джаве удобнее, чем на BF, и удобнее для всех. А вот удобство Java относительно C++ — уже вопрос личных предпочтений.

А еще, не стоит забывать про производительность. Все мы воспеваем мудрость JIT-компилятора, радуемся синтетическим бенчмаркам, показывающим преимущество производительности Java над Native-кодом, но, все-таки, покажите мне хоть одно реальное приложение на Java, которое быстрее своих аналогов (в полном смысле, а не с другими алгоритмами) на C++?
UFO just landed and posted this here
Всё относительно. Например, если нужна производительность, то С++ код начинает превращаться в С-код.
«все что можно трогать пальцами — лучше трогать пальцами»
Работаю над ААА игровыми проектами, где миллионы строк ООП кода. И чем больше я с ним работаю, тем больше ненавижу этот ООП. Считаю, что «С с классами» это золотая середина. Аскетичность С позволяет значительно увеличить читаемость кода. Не мне говорить о том, сколько времени мы проводим за чтением кода, особенно на проектах, которые пишут сотни программистов. В купе с некоторыми плюшками С++ так же помогает, к примеру шаблоны для контейнеров. В монструозных ООП системах невозможно понять флоу программы исключительно чтением кода -> обязательно необходимо запустить под отладчиком.

во-вторых, для этого случая:
… то правильнее то же самое написать так:
class Foo  
{  
public:  
    static void foo();  
};  
...  
Foo::foo(); 


ИМХО, лучше писать так:
struct Foo  
{  
    static void foo();  
};  
...  
Foo::foo(); 
Тогда уж так:
namespace Foo  
{  
    static void foo();  
};  
...  
Foo::foo(); 

Зачем использовать class/struct для такой вещи, как пространство имён? Для этого есть специальный инструмент — namespace
Ваш вариант был предложен автором поста. Я лишь предложил замену одному из его вариантов, так как он выглядит более логичным — не надо указывать «public:»
Вообще, структуры/классы со статическими функциями можно трактовать как пространства имен, которые нельзя расширять, т.е. нельзя сделать так:
namespace Foo {
    ...
}
...
namespace Foo {
    ....
}
автор, почему вы мыслите так категорично: только С++, только хардкор? Я сам программирую 6 лет и если можно обойтись без создания классов и объектов, то почему бы и нет. Создавать на каждую функцию по классу(или ОМГ объекту) ИМХО бред. Переместил все в namespace и радуешься жизни.
«Не плодите сущностей сверх необходимости» (с)
Ваши слова напомнили мне статью на хабре, которую, к сожалению найти не смог, т.к. не помню названия. Но в основной идее было примерно следующее: если в классе есть только конструктор и одна функция — не пишите класс, пишите просто функцию.
Автор, а теперь признайтесь честно, сколько лет из 20 вы программируете на Java?
Потому что все ваши рассуждения, типичны для Java-разработчиков,
которые перешли на С++, но продолжают рассуждать в терминах Java,
«Все есть класс»
«Функторы»
«Поголовный try...catch»
«Наследние main»
try… catch в примерах как раз очень в тему. В main почти всегда надо его писать: есть тонкость, заключающаяся в том, что если исключение бросается вне try блока, компилятор имеет право сразу вызвать std::terminate и не разматывать стек. Но вы то хотите, чтобы деструкторы, по возможности, отработали.
интересно, а куда полетят исключения в коде, который до main исполняется? А такой код почти всегда есть!
До main, это в конструкторах глобальных объектов? Приведут к вызову std::terminate т.к. их никто не ловит. Деструкторы уже создавшихся глобальных объектов могут быть вызваны, а могут и не вызываться.
Поэтому, а также потому, что порядок создания глобальных объектов не определен, лучше стараться их не создавать.
Но на самом деле исключения в конструкторах это моветон. Но можно же наверное вызвать свободную функцию из конструктора. а она возьмет да и кинет исключение!
Нет, исключения в конструкторах — корректная практика. RAII, опять же.

Вот в деструкторах — это да.
UFO just landed and posted this here
Или вот какой замечательный инструмент VTune я уже не говорю о компиляторах С/С++ и Fortran, но какой жуткий у них UI, по крайней мере пару лет назад был.
У VTune мне нравится UX — вполне функциональненько.
А у ICC я не видел интерфейса, это же CLI, IDE использую MS VC/
Ой не хорошо как опускаться до подобных вещей и судить о квалификации! всех! программистов в Интел опираясь на одну только статью. Ой не хорошо.

Скажу как интеловский «не очень» программист. Я работаю тут уже достаточно долго и еще ни разу не видел ни одного front-end инженера и не слышал о таком. Честное словно, я понятия не имею кто все эти люди, которые рисуют эти ужасные интерфейсы. Есть подозрение, что все это отдают на контракт/аутсорс или студентам.

К слову, интерфейсы от IBM (в корпоративном секторе) не менее ужасны. Был опыт использования такого тула. Кошмар.
UFO just landed and posted this here
UFO just landed and posted this here
«Я ничего не понял из того, что ты сказал, но ты заговорил, и достучался до моего сердца»(с) Джей и молчаливый Боб.
Трудно спорить с «сотрудником Интела», но я все таки считаю С++ надмножеством С, и «чистый С++» оксюмороном.
Все должно быть в меру. И для каждой конкретной задачи — свой прием. А притягивать всё и вся за уши к ООП только лишь потому, что это должно быть ООП — бессмысленно.
Сколько я видел статических функций внутри классов в Java и C#, даже в стандартных библиотеках, которые там только потому, что по-другому в этих языках никак. Выглядит неестественно.
Есть смысл в превращении функций в функторы — для облегчения встраивания, иначе пришлось бы передавать указатель на функцию. Но все равно с точки зрения дизайна они обычными функциями и остались, если не хранят состояния.
За что люблю C++: есть задача — выбирай подходящий способ решения. В погоне за чистым ООП, теперь может и функциональную парадигму запретить? Лямбды исключить? И связывание аргументов?
>Программист — это тот, кто может решить поставленную алгоритмическую задачу в заявленный срок с заданным уровнем качества.

Тяжелый случай. Впрочем я думаю, что у 90% и этого нет. Но вот надо немного поработать над текстом. Устремим качество в бесконечность и срок в ноль. Что мы имеем? Кто программист? Программирование есть процесс поиска компромисса. Я никогда не доволен тем, что отдаю заказчику. Всегда есть пространство для улучшения. Но речь не обо мне. Я все-таки не инженер Интел
Если автор рассчитывал, что данная статья может хоть какого-либо адекватного человека в здравом уме, твёрдой памяти и без садомазохистских наклонностей мотивировать к использованию C++ (причём в том самом, единственно верном виде, который автор считает правильным), то для дальнейшей мотивации мне остаётся линкануть только вот это:

harmful.cat-v.org/software/c++/linus
Видимо, ни стаж, ни место работы не спасают от гавнотопиков.

Судя по анализу первых двух абзацев, у тебя возник конфликт на работе, причём со старшим товарищем, считающим что имеет право использовать процедурный стиль программирования при написании кода на «чистом» C++.

Выводы,
1) ты салага
2) не знаешь C++
3) нахватался книжек про шаблоны и бежишь хвастаться всем и каждому
4) страдаешь на данный момент острой формой «звёздной болезни».

Осень, что-ж, бывает.
Не хочу защищать позицию автора статьи про «Ц с классами», но все-таки, всегда было интересно:
зачем писать такие оскорбительные комментарии? Если вы не согласны с мнением, можно же это выразить не в обидной, а, так сказать, в интеллигентной форме: «не согласен с тем-то и тем-то, потому что 1,2,3 и т.п.». Вы бы еще 5 пунктом добавили «КГАМ».
Дурно пахнущие крики чужой души интеллигентно салфеточками не подтираю.

Оскорбление и обида — это признание слабости своей позиции, демонстрация беспомощности. Если не можете ответить — то обижаетесь.

Это вообще чувства, которые особенно ярко воспринимаются в глубоком детстве. У многих с годами проходит.

А мне не понятно, с чего вы взяли, что эти высказывания оскорбительны для автора топика? Или они оскорбительны для Вас? Тогда прошу не подменять субъект, и не прикрываться маской благородного заступничества.

Насрал так насрал.

Или вам процитировать?
Скажу прямо: вместо «оскорбительных и обидных», читайте «хамских и грубых». На этот счет есть пункт правил.
По поводу C++. При разговоре о C++ автор даже не заикнулся, о каком C++ говорится. gcc (какой версии), C++0x или C/C++ 92, или Microsoft C++, у которого есть своя позиция на счёт реализации компиляции с этого языка. Я могу сказать, что я использовал Watcom C++, и Borland C++, так что не нужно тут всех стращать правилами. Автор кричит о произволе в использовании возможностей языка. Подумайте, об этом крике души?

Все эти реализации стандарта различны между собой:

Watcom C++
Borland C++/C++ Builder (Borland / Inprise / Codgear / Embracadero)
Intel C/C++ (всех версий)
gcc C++,
Microsof Visual C++

Все они различаются во многом, особенно от Watcom C++:

-оптимизация,
-поддержка шаблонов
-реализация intrinsic / inline функций
-поддержка анонимных структур
-поддержка анонимных функций
-поддержка C
-поддержка своих собственных расширений языка C++ (Watcom. Microsoft C++ compiler).

Если хотите истории, давайте поднимем историю в виде Watcom C++, если здесь еще есть люди, родившиеся до того, как эта компания канула в лету, и старики, которые меня поддержат, а то что кто-то жует сопли по пустячным поводам — ну уж извините. Не было вложенных шаблонов, никто не жаловался на > > да и не только. Просто сравните с черновиком стандарта c0++x и возрадуйтесь тому, как много стало возможностей.

Вы знаете, не всегда было время, когда существовал boost, auto_ptr и подобное? Ну писали люди на голом C в Watcom С++, и что? Ну, появилась возможность передавать ссылки в шаблонизированный конструктор, ну и началось — где надо и где не надо, по поводу и без. Как только стало возможным использовать по настоящему шаблоны функций, нашлись умельцы, которые сделали из этого чёрт знает что. Открылась возможность писать шаблонизированные классы — и пожалуйста, вместо простого и понятного кода — горы синтаксического сахара, красивейшего с точки зрения повторного использования кода, однако дорогой ценой, из-за чего даже оптимизирующий компилятор иногда переварить эти классы не в состоянии (например, потому что на момент компиляции, не все типы статически определены или компилятор просто не принимает синтаксически корректную в других компиляторах структуру или класс).

В общем, мне нравится всё многообразие C/C++ и вариантов его использования. Просто надо понимать, что не всё в этом мире теперь строится на дженериках (generics), не бывает идеальных решений, и не нужно везде применять повторное использование кода, абстрагированное до утопии.

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

Ну, правда, не надо.

Пусть себе живёт.

А тем, кто программировал на Watcom C++, всем большой привет.

:)

Только сейчас увидел комментарий ваш. Небольшой даже экскурс в историю получился, мне было полезно почитать, как начинающему С++нику. Я даже с вами соглашусь по сабжу. И не смотря на это, как-то вы грубо в том комменте, и это вызвало негодование у меня.
>Кстати об обстоятельствах и задачах. Как вы думаете, кто такой «настоящий программист»? Нет, не хакер. И не тот, кто знает о языках программирования ВСЕ. И даже не тот, кто способен написать самую «крутую» программу.

Настоящий программист не пишет подобных говнотопиков, это факт.
http://odesskiy.com/zhvanetskiy-tom-1/sila-slova.html
МИХАИЛ ЖВАНЕЦКИЙ — ТОМ 1:
«Они мне сказали: «Ты, Федя, пропагандист, полтора часа говоришь – непонятно о чем». Кому непонятно? Вот я скажу вам, а вы мне скажите: вам понятно или нет? Я решил сказать, потому что я старше их не по возрасту, а по годам. Конечно, да, конечно, надо выражаться, чтоб тебя понимали и чтоб ты сам себя понимал. Да! И чтобы все остальные – нет, не забывали о силе слова. Потому что слово – это сила, слово – это… О! Слово понимает каждый, но ему нужно объяснить! Речь пойдет о слове, о силе слова. Это сила! О чем я ниже буду перечислять....»
Прочитав, Intel, заголовок и «Вместе с коллегами пишу GPA.» ожидал кучи блок-схем, алгоритмов, идей, интересного и сложного кода. Но вы быстро вернули меня на землю.
С++ — очень неоднозначный язык. Не знакомился с новым стандартом, но прошлый уж точно выглядит как fail. Например, чисто виртуальная функция, наличие хотя бы одной из которых в классе говорит о его абстрактности… Просто гениально… Или такая вот фишечка — необходимость ставить пробел между > и > в конструкциях типа teplate_угловая_скобка_парсер_сука_typename T> >. Синтаксический фэйл (в новом стандарте вроде как поправили). Или проблема разделения шаблонов… Константные указатели на константу. Тип autoptr.
Но писать на нем интересно было. Чем-то он манит — своим хитрым строением, своей гибкостью. НО! Назвать C++ ООП-языком после моего опыта работы с C# я никак не могу.
Например, чисто виртуальная функция, наличие хотя бы одной из которых в классе говорит о его абстрактности… Просто гениально…

А что вы предлагаете? Класс неполон, у него реализации целиком нет, как его создавать? Должен прозвенеть звоночек, что вы хотите странного и надо поискать альтернативные варианты. Если вы абсолютно уверены в том, что это вам нужно, поискали альтернативные решения и не нашли их, скажите об этом компилятору, объявите реализацию, кидающую исключение.
Или такая вот фишечка — необходимость ставить пробел между > и > в конструкциях типа… Синтаксический фэйл (в новом стандарте вроде как поправили).

А вы знаете, почему так? Про то, что такое лексический и семантический парсеры, где они в компиляторе и как связаны друг с другом?
Или проблема разделения шаблонов

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

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

Клёвый тип.
1) Например, снабдить класс ключевым словом abstract?
2) Я прекрасно представляю, что такое лексер и парсер, и даже писал их (вручную и при помощи генератора на основе расширенной грамматики Бэкуса-Наура).
3) Ну вот — ещё одна неполноценность. А я хочу иметь возможность выносить — и все тут. И да — для всех компиляторов существует пара моделей разделения — модель включения и явного инстанцирования, но они не полноценные. Читал в книжке, посвященной шаблонам (на 500(!) страниц — вот это моща для какой-то конструкции в языке)
4) Понятно, что ссылками. Но понимать эту константность надо
5) Насколько я помню, там были всякие проблемы при его использовании, например, они возникают, если пихать переменные такого типа в контейнеры STL.
Например, снабдить класс ключевым словом abstract?

Ну зачем вам инстанциировать неполноценный класс? Он же частично сломан. Но если очень хочется, то такое желание должно быть исключением, а по-умолчанию создание такого класса должно быть запрещено. Так что нужно не слово abstract, а его антоним.
Вы же знаете, почему в языки стараются не добавлять новые ключевые слова?
Я прекрасно представляю, что такое лексер и парсер, и даже писал их (вручную и при помощи генератора на основе расширенной грамматики Бэкуса-Наура).

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

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

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

Да, были. Правда ловились на этапе компиляции, к счастью. Теперь есть unique_ptr, который точно такой же, только без этих проблем.
Разумеется, для любого фэйла в языке есть объяснение. Тем не менее фэйлом он и останется. В этом плане перспективно смотрится язык D.
Язык D — это какой-то дикий замес синтаксического сахара без цели. Типа «а давайте мы разрешим еще делать вот так средствами языка»
Вы чуть-чуть ошиблись. unique_ptr вываливает ошибку компиляции, там где auto_ptr лажал уже в рантайм. Это стало возможно благодаря тому, что unique_ptr может отобрать себе во владение указатель только у xvalue (T&&). Классического копирующего конструктора и operator= он намеренно не имеет, поэтому случайно скопировать unique_ptr не получается на этапе компиляции.
А стандартные алгоритмы теперь явно говорят, что они перемещают значение посредством std::move или делают копию.
Спасибо, я действительно ошибся. Уже год как перестал использовать auto_ptr и детали подзабылись.
Объясните мне, балбесу, из каких соображений в «чисто-плюсовом» примере полностью игнорируется значение возвращаемое методом App::Run и main всегда возвращает 0, игнорируя потенциально возникшие исключения в программе?
Исключения там в примере ловятся через try/catch
Спасибо за «На этом языке простые и банальные вещи пишутся очень громоздкими и запутанными конструкциями. Однако именно эти конструкции помогают сделать в этом языке сложные вещи просто.»
Очень многое и много где объясняет. Например, когда сравнивают разныей фреймворки, часто вижу суждение, что то-то делается с меньшим количеством строк, чем в другом. Но сравнение это всегда происходит на программах типа Hello, world, что некорректно.

Жду продолжения.
Правильно ли я понял, что в Intel любят, уважают и успешно применяют плюсовые исключения в продакшен?
Про intel не скажу, а в яндексе любят и уважают. Но свято соблюдается правило, что исключение — это именно исключение. Оно не должно возникать при нормальной работе сервиса/программы. Соответственно при нормальном ходе вычисления мы ничего не платим на них (у gcc вход в try блок не требует runtime вычислений), в том числе не тратим время на обработку кодов возврата. А в случае ошибок, которые довольно редки, допустимо тратить значительно больше времени на их анализ.
Статья ни о чём. Всё что написано найдётся в любой книге по С++, которые автор рекомендует к прочтению до статьи. Смысла читать её нет.
Это крик души, как и было анонсировано.

Анонсированный крик души. Захабрено
Sign up to leave a comment.