Чистый C++

от автора

Давайте знакомиться.

Я — Серега. (На фото — не я). Работаю в Intel. Вместе с коллегами пишу GPA. Программирую вот уже скоро 20 лет как. Ну, это если считать со школы. Последнее время накопилось много разных мыслей, которыми хочется с кем-то поделиться. Рассказать кому-то о том, что такое хорошо, а что такое плохо. Рассказывать можно и пустоте (так даже спокойней, никто не отвлекает и не суется со Своим Самым Правильным мнением), но это не очень эффективно. Поэтому буду сливать свои мысли сюда. Вдруг кому-нибудь пригодится…

В качестве введения

Многие считают, что есть два родственных языка — C и C++. При этом C++ — это якобы тот же C, только с двумя плюсами, т.е. ООП. Это очень распространенное заблуждение. «На самом деле все не так». C и С++ — это совершенно разные языки, не имеющие между собой практически ничего общего. Однако исторически так сложилось, что C++ синтаксически совместим с C, т.е. может компилировать программы, написанные на C. Из-за этой особенности четкая грань между языками отсутствует и существует множество кода, написанного на жуткой смеси этих языков. Причем пропорция этой смеси может меняться даже в рамках одной программы у одного автора. Будем называть подобное творчество языком «Ц с классами». Иногда использование «Ц с классами» может быть осознанным выбором, продиктованным обстоятельствами и решаемой задачей. К сожалению, чаще всего подобный код просто демонстрирует огромную разницу между амбициями и реальными способностями его автора. Если вы еще только в самом начале пути, лучше возьмите что-то более строгое, например чистый C или Pascal. А потом приходите сюда, когда в этих языках станет тесно.

Кстати об обстоятельствах и задачах. Как вы думаете, кто такой «настоящий программист»? Нет, не хакер. И не тот, кто знает о языках программирования ВСЕ. И даже не тот, кто способен написать самую «крутую» программу. Программист — это тот, кто может решить поставленную алгоритмическую задачу в заявленный срок с заданным уровнем качества. Перечитайте это определение несколько раз медленно, обдумывая каждое слово. Попытайтесь понять такую простую вещь, что без задачи, сроков и качества программирования не бывает! Это может быть творчество, обучение, эксперимент, исследование, что угодно еще, но только не программирование. Потому что результатом программирования является программа, которая служит людям, которая, начиная с момента своего создания, изо дня в день, многократно решает для них свою задачу. Даже википедия в определении термина программист 3 раза упоминает слово “задача”! Поймите, никому не нужна идеальная программа, написанная за бесконечно долгое время. Люди с большей охотой будут пользоваться хоть чем-то, что решает их проблемы здесь и сейчас, ну, в крайнем случае, завтра. Так же мало кто будет пользоваться дурно пахнущей поделкой, сляпанной кое-как, когда вокруг уже существует множество более простых и приятных решений ихних проблем. Практически никто не будет пользоваться «прикольной безделушкой» долгое время. Разнообразные бегающие по экрану звери, навороченные спецэффекты анимации окон и менюшек, Самые Правильные Операционные Системы и т.п. — все это барахло обречено создавать ВАУ эффект у молодого поколения, после чего с почетом уходить в небытие. Я могу рассказать про обеспечение качества. Объяснить кое-что про сроки. Но задачи придется искать самостоятельно.

Думаю, настало время поговорить за C++. Надеюсь вы уже прочли толстые и умные книжки про этот язык, написали и скомпилировали Свою Первую Программу? Если еще нет — идите, читайте и пишите, поговорим, когда будете понимать хотя бы синтаксис языка. Если вы еще тут, то наверное уже знаете — этот язык поддерживает ООП на уровне синтаксиса. Однако следует для себя понять одну важную вещь, чтобы не скатиться в «Ц с классами». В C++ все есть класс. Попробую на пальцах:

void foo();   ...   foo(); 

Это самый что ни на есть C. Даже без классов. Если мы пишем программу на C++, то правильнее то же самое написать так:

class Foo   {   public:       static void foo();   };   ...   Foo::foo();  

Еще иногда бывает удобно сделать вот так:

class foo   {   public:       foo();   };   ...   foo();  

Или даже вот так:

class Foo   {   public:       void operator()();   };   ...   Foo foo;   foo();  

В исключительных случаях, можно и так:

namespace Foo   {       void foo();   }   ...   Foo::foo();  

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

class IFoo   {   public:       virtual void foo() = 0;   };  

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

template <typename TypeA> class DoIt { private:     TypeA m_it; public:     template<typename TypeB> DoIt(TypeB it) : m_it(it) {}     operator TypeA() {return m_it;} }; ... TypeC c;   TypeD d = DoIt< TypeD >(c);  

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

Есть такая игра слов: Чем больше сыра — тем больше в нем дыр, чем больше в сыре дыр — тем меньше в нем собственно сыра. В результате чем больше сыра — тем меньше сыра. С C++ все именно так и происходит. На этом языке простые и банальные вещи пишутся очень громоздкими и запутанными конструкциями. Однако именно эти конструкции помогают сделать в этом языке сложные вещи просто. Возьмем например вашу первую программу на «Ц с классами»

#include <iostream>   int main(void)   {       std::cout << "Здраствуй Мир!!!\n";       std::cout << "До свиданья\n";       return 0;   }  

и сделаем из нее программу на C++:

#include <iostream>   class App   { public:     int Run()       {           std::cout << "Здраствуй Мир!!!\n";           return 0;       }       ~App()       {           std::cout << "До свиданья.\n";       }   };  int main(void)   {       try     {         App().Run();     }     catch(...)     {         throw;     }     return 0; }  

Функция main для нас неизбежное наследие С и избавиться от нее без лишних проблем тяжело. Будем считать такую ее реализацию частью языка. Теперь представим, что нам нужно добавить в нашу программу сложную вещь:

#include <iostream>   #include <stdexcept>   class App   { public:     int Run()       {           std::cout << "Здраствуй Мир!!!\n";           throw std::runtime_error("Что-то плохое в нашей программе случилось.");           return 0;       }       ~App()       {           std::cout << "До свиданья.\n";       }   };  int main(void)   {     try     {         App().Run();     }     catch(...)     {         throw;     }     return 0; }  

В последнем варианте наша программа будет обрушена исключением, выброшенным откуда-то изнутри. Однако, несмотря на плохой финал, она сможет корректно выполнить свою завершающую часть. Как можно заметить, сложность никак не изменила нашей программы. Именно такие фокусы и позволяет делать «настоящий» C++. Если вы думаете, что всегда и все можно обложить секциями try… catch — у меня для вас плохие новости. Такой код будет практически невозможно поддерживать.

Домашнее задание

Прочитать описание таких вещей, как Паттерны.

ссылка на оригинал статьи http://habrahabr.ru/company/intel/blog/156863/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *