В этой статье мы рассмотрим простое и понятное введение в С++. Хотя C++ кажется сложным, но это происходит потому, что при его описании используют много туманных абстракций и лишних терминов.
Мы же рассмотрим C++ без всякой философии, с точки зрения программиста-практика.
1. Сравнение С и С++
Начнем с хорошей новости. Язык C++ — это клон языка C, в который были добавлены средства работы с классами. Поэтому, если вы знаете язык C, то вы уже на 90% знаете C++.
Первоначально C++ так и назывался «Си с классами», но потом решили дать более «си-шное» название, то есть добавили единичку к C. Поэтому нам нужно разобраться только с программированием в классах. Что мы сейчас и сделаем
2. Создание нового проекта
Запустите CodeBlocks, создайте новый проект и выберите шаблон «Console application».
Нажмите «Перейти».
Выберите C++ и нажмите Next.
Укажите папку и имя файла и нажмите Next.
Нажмите Finish. Вы получили минимальную программу на C++.
Нажмите F9 для компиляции и запуска программы.
Все работает.
3. Пример на С
Так как C++ — это клон C, то мы можем писать код, как обычно мы это делаем в языке C. Сделаем это, чтобы потом сравнить отличия. Напишем следующую программу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <iostream> using namespace std; int Sum(int x, int y) { return x + y; } int main() { int x = 5; int y = 10; int z = Sum(x,y); cout << "Sum = " << z << endl; return 0; } |
В этой программе все просто. Есть две переменных x и y, и есть функция Sum, которая складывает два числа. Обозначение «cout <<» можно рассматривать как вариант функции printf.
Скопируйте этот текст и убедитесь, что все работает.
4. Главный секрет C++
А теперь мы напишем ту же самую программу, но уже с классами. И для этого нам нужно знать главный секрет C++.
Как видите, секрет достаточно прост. Запишем нашу программу но уже с помощью класса:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <iostream> using namespace std; class Summa { public: int x; int y; int Sum() { return x + y; } }; int main() { // Программирование с классами Summa s; s.x = 6; s.y = 12; z = s.Sum(); cout << "Sum = " << z << endl; return 0; } |
Итак, что мы сделали?
Мы создали класс Summa с помощью ключевого слова class. Но класс — это просто шаблон. Для использования класса нужно получить экземпляр класса. Это мы сделали в строке «Summa s;». После того, как эта строка выполнится, мы получим экземпляр класса, к которому же можно обращаться. При этом переменные класса называются свойства, а функции класса называются методы.
Обратите внимание, что функция Sum не требует указания параметров, потому она берет эти параметры из свойств класса.
Обращение к свойствам и методам производится с помощью точки. Например, «s.x» — означает, что мы обращаемся к свойству экземпляра s класса Summa.
Ключевое слово public означает, что свойства и методы класса доступны вне класса.
5. Наследование классов
И теперь познакомимся с ключевым понятием программирования в классах — наследованием.
Опять же сравним с чистым Си.
Допустим, что нам нужно не только суммировать числа, но и удвоить результат. В языке Си один вариантов это сделать — использовать вложенную функцию:
1 2 3 4 5 6 7 8 9 |
int Sum(int x, int y) { return x + y; } int Sum2(int x, int y) { return Sum(x,y) * 2; } |
В этом примере функция Sum2 использует вызов функции Sum. Важный момент здесь в том, что те параметры, которые были переданы на вход функции Sum2 просто передаются на вход функции Sum. Такая сквозная передача параметров — это обычная практика C.
Но мы видели, что в классе мы параметры не использовали. Как же их передать? Для этого в C++ используется наследование.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include <iostream> using namespace std; class Summa { public: int x; int y; int Sum() { return x + y; } }; class Summa2 : public Summa { public: int Sum2() { return Sum() * 2; } }; int main() { // Программирование с классами Summa s; s.x = 6; s.y = 12; cout << "Sum = " << s.Sum() << endl; // Наследование класса Summa2 s2; s2.x = 7; s2.y = 13; cout << "Sum = " << s2.Sum2() << endl; return 0; } |
В этом примере в строке «class Summa2 : public Summa» мы указываем, что класс Summa2 наследуется от класса Summa. Это позволяет обратиться к свойствам и методам наследуемого класса.
Там где в С используются вложенные функции, там в C++ используются вложенные классы.
На самом деле, из-за того, что класс объединяет данные и код, возникает масса проблем, поэтому с классами связаны еще многие ключевые слова. Но суть программирования в классах они не меняют, а только вносят сумбур в головы начинающих программистов.
И несколько слов надо сказать про рекламную чепуху вокруг C++. Обычно в книжках про C++ пишут о том, что программирование в классах позволяет не больше и не меньше как моделировать весь мир. Это весьма странное заявление. Любая программа — это код и данные. Их можно написать отдельно, можно объединить в общую структуру, но от этого принципиально ничего не меняется. Любая программа на С может быть переписана в классах, а любая программа в классах может быть переписана на С.
То есть C++ не дает программисту какого-то ключевого преимущества, именно поэтому язык C++, который позиционировался как замена C, так и не смог его заменить. Для интереса можно посмотреть TIOBE-рейтинг этих языков в сентябре 2018.
Уважаемый Константин!
Вы написали: «Класс – это объединение кода и данных».
А В ваших «классах» нет данных, там лишь публичные переменные, которые лишают Ваш код одной из главных фишек ООП — объект = чёрный ящик, нельзя ковыряться во внутренностях объекта.
https://jug.ru/2016/09/bugayenko-west/
>А В ваших “классах” нет данных
Переменные — это тоже данные. Добавьте массив — суть примера не изменится.
>там лишь публичные переменные
Добавьте приватную переменнную — суть примера не изменится.
>из главных фишек ООП объект = чёрный ящик, нельзя ковыряться во внутренностях объекта.
Это не фишка ООП, а обычная практика языка Си (да и других языков). Если программист — это автор объекта, то он может «ковыряться во внутренностях» сколько угодно. Если программист не автор объекта, а объект скрыт в модуле, то доступа к нему нет.
Уважаемый Константин!
Если Вы считаете, что «автор объекта, то он может “ковыряться во внутренностях” , то Вы лишили себя инкапсуляции. А лишили Вы себя, как и многие другие, потому что уже научились хорошо программировать процедурно. Без инкапсуляции в ООП вы как рычаг без точки опоры. https://inexsu.wordpress.com/2018/07/28/%D0%BE%D0%BE%D0%BF-%D1%80%D0%B0%D0%B7%D1%80%D1%83%D1%88%D0%B8%D1%82%D1%8C-%D1%81%D0%BE%D0%B1%D0%BE%D1%80-%D0%BF%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B8%D1%82%D1%8C-%D0%B1%D0%B0%D0%B7%D0%B0%D1%80/
До капсуляции дело еще не дошло. Это просто описание, что такое класс.
И насчет «ковыряния во внутренностях». Инкапсуляция (encapsulation) — технология объединения собственно данных и методов их обработки (в одном компоненте). Это и показано в приводимом коде, где в классе размещены и данные (переменные x и y) и метод их обработки (функция Sum()). А что касается недоступности внутренностей объекта, так это называется сокрытием (hiding). Причём в программировании это означает скрытие внутренней реализации компонента от других компонентов, а не от программиста. Например, доступ к скрытой переменной может предоставляться не напрямую, а с помощью методов для чтения (геттер) и изменения (сеттер) её значения. По аналогии ООП с окружающими нас объектами: внутренности бытовой техники доступны тем, кто их создаёт, а потом их одевают в кожухи, защищающие их от непосредственного контакта с другими приборами, сетью, а также ограничивая доступ простых пользователей, и предоставляя для этого специальные интерфейсы, пульты управления… Если надо для ремота, то ремонтёр может снять и кожух, и даже разобрать на детали…
Так что, если в сообществе C++, как и Java, и принято рассматривать инкапсуляцию без сокрытия как неполноценную, но считать это одним и тем же или подменять одно другим — заблуждение. Тем более есть языки, например, Smalltalk, Python, где реализуют инкапсуляцию в полной мере, не предусматривая возможности скрытия в принципе, и есть языки, такие как Standard ML, OCaml, где жёстко разделяют эти понятия и предоставляют их в семантически различном виде.
Странно, что Михаил критикует данную статью Константина на несоответствие кода объектно-ориентированному программированию. Ведь статья посвящена классам в языке С++, а не ООП в языке С++, тогда как язык С++ мультипарадигмальный и допускает кроме объектно-ориентированного программирования обобщённое, процедурное и метапрограммирование. Причём приводит ссылку на статью «Классы — это не объектно»: интервью Егора Бугаенко с Дэвидом Уэстом (https://jug.ru/2016/09/bugayenko-west/), где, согласно этой статье, C++ не задумывался как объектно-ориентированный, классы — это не объектно-ориентированно и не имеют отношения к объектам, а самый объектно-ориентированный язык — Self. Вот цитаты из этой статьи:
«Но вообще-то C++ не объектно-ориентированный, он никогда и не задумывался таким. Если почитать, что Страструп писал во время создания C++ … Но он ни разу не называет его объектно-ориентированным языком. Другие люди сказали: «Ну, у вас в C++ классы, значит, вы объектно-ориентированные, как Smalltalk». Но ООП не было задумкой.»
«Так что, например, всё понятие класса — это не объектно-ориентированно. Классы не имеют никакого отношения к объектам. Но они были способом эффективно хранить код, позволяя вносить изменения в одном месте вместо многих сразу.
Ближайшее, что когда-либо было к настоящему ОО-языку — Self. Там нет ничего, кроме объектов, там только один класс, или один тип сущностей, и эта сущность — объекты. Там можно использовать методы и переменные, можно клонировать, но нет такого явления, как класс.»
А статья интересная! Ещё одна цитата:
Дэвид: Ну, как я попытался сказать в книге, класс ничего не должен делать. В Smalltalk есть методы классов, но не надо их использовать. Их использование — возвращение к командному способу мышления. Вы берёте то, что должно быть ответственностью объектов, и по каким-то причинам пытаетесь запихнуть это всё в класс. И в итоге класс делает многое за объекты.
Все сиплюcплюcшники рекомендуют переменные делать недоступными, и по умолчанию так и есть, по умолчанию доступны методы, а вот в си уже изначально присутствует структура данных struct, не помню, может ли входить метод в структуру языка си, но в структуре си++ как раз наоборот, данные доступны по умолчанию и требуют объявления public а вот методы-функции не доступны по умолчанию, но у класса присутствует конструктор и деструктор, вот что важно, это метод, который есть и по умолчанию и он создаёт объект и может быть определён программистом, а вот у структур такого нету… Казалось бы структура и класс почти одно и тоже, но не совсем. Я бы вот хотел дополнить, что класс это всё таки не шаблон, строго говоря, а новый тип данных, или тип объекта, в котором присутствует «жизнь» не только как набор переменных и констант этаких «существительных», но и «глаголов» , то есть действий с данными. Деструктор тоже можно определить по-своему. А шаблонами в языках программирования называют записи для компиляторов с использованием инструкций для компилятора (макрокоманды), на стадии получения исполняемого или объектного кода, и ещё шаблоном можно бы назвать определение виртуальных функций…. Вообще-то есть у Си++ библиотека шаблонов, шаблонное программирование, там много новеньких понятий для программиста на фортране-79. В общем программирование с классами, это объектно-ориентированное программирование, в котором можно определить свои куски кодов, которые создаются и уничтожаются в программе по мере использования и даже если программист не знает текста а знает что делает объект некоего класса, на основе одного класса он может определить объект своего класса с помощью наследования и работать с ним, они наслаиваются и наслаиваются эти классы.
>класс это всё таки не шаблон
В данной статье слово «шаблон» используется в общепринятом смысле, а не в смысле языка C++.
>программирование с классами, это объектно-ориентированное программирование
Нет. Объектно-ориентированное программирование — это стиль программирования, который можно использовать, а можно и не использовать при программировании с классами.
Автор застрял в светлом прошлом и явно не хочет смотреть в будущее,а примеры с наследованием особенно неудачные. Аналогия с генетикой и живыми объектами природы,когда каждый из них получал новые свойства и меры,которыми эти объекты используют,чтобы воплощать свои способности.
И ещё,эти два языка нельзя рассматривать по отдельности( C и C++) -они применяются вместе в одном проекте по мере необходимости.Так как по традиции все операционные системы написаны на чистом Си,то конечно он будет лидировать.
Но почему то до по явления C# в прикладом программировании был популярен Pascal Delphi.
>примеры с наследованием особенно неудачные.
Суть примеров — показать, как устроен язык C++.
Если не нравится, то приведите удачный пример наследования, только не абстрактные рассуждения, а конкретный код. Аналогии — это хорошо, но программисту платят не за аналогии, а за написание программ.
>они применяются вместе в одном проекте по мере необходимости.
При программировании в стиле ООП их совмещать нельзя. А так, конечно, при желании можно написать программу, хоть на десяти языках сразу.
«Аналогия с генетикой и живыми объектами природы,когда каждый из них получал новые свойства и меры,которыми эти объекты используют,чтобы воплощать свои способности.» — тут соглашусь. Пример приведенный автором напомнил мне школьный курс, начала 90-х. Хорошим примером использования ООП является примерны реального мира.
Но далее у меня уже серьезные разногласия возникают:
1) «И ещё,эти два языка нельзя рассматривать по отдельности( C и C++) -они применяются вместе в одном проекте по мере необходимости.» — это где вы такое видели? Ну точнее кончено могут быть программы написанные отдельно на C и отдельно на C++, в рамках одного проекта, но вместе код не может быть использован. Не встречал подобных IDE и компиляторов. Может быть конечно Eclipse или NetBean, последних версий, умеют осуществлять такую сборку…
2) «Так как по традиции все операционные системы написаны на чистом Си» — давайте будем честны, далеко не все. Раньше все ОС писали на ассемблере.
Спасибо большое. Очень методично.
Изучаю С++ на английском, на этом простом примере всё стало ясно, в примерах данных в колледже очень все сложно и я потеряла суть простой схемы. Спасибо, Константин, за работу, продолжаю читать Ваши материалы.
Рад, что Вам понравилось!
изучаю Си и ++ что все понятно спасибо Константин
мне подробно скажите про библиотека
Прекрасное введение в С++ на уровне аналогий.
А комментарии «знатоков» явно не по делу. Особенно меня умилил комментарий о С# и Дельфи. Автор путает грешное с праведным. Речь идет о программировании под windows. Мелкософт для этого изобрел win API с графической системой gdi, между нами говоря, очень неудобной. Объектно ориентированное программирование ее не спасло. Одновременно Borland разработала графическую систему bgi. Она-то и реализована в Дельфях.
Когда Borland преставилась одного из разработчиков bgi перекупила мелкософт. Он написал windows forms, ясное дело под влиянием bgi. А для ее реализации сварганили С#. Так что С# в какой-то мере потомок Дельфи.
Да, как-то набросились некоторые «знатоки» с критикой «не в строчку». А может вы, Константин, нарушили «Правило чистой энергии» в своей критике языка C++, когда сравнивали его с чистым C?
В принципе, сайт для начинающих программировать. А начинающим ошибаться простительно. Как писал Страуструп: «слишком многие ошибочно полагают, что их первый язык программирования — самый лучший. Язык С++ во многих отношениях прекрасный язык, но он не идеален; впрочем, то же самое можно сказать о любом языке программирования.».
Если посмотреть на статистику, то C++ так и не смог приблизиться к популярности к C. Я объясняю, почему так произошло.
Как я прочитал на Хабрахабре: Объектно-ориентированное программирование это стиль программирования, а классы — это синтаксический сахар, предоставляемый языком программирования, таким как C++. То есть программировать с использованием классов ещё не означает ООП, а в стиле ООП можно программировать и без использования классов.
Объектно-ориентированное программирование это целая парадигма, в которой даже несколько подходов и соответсвующих стилей (в зависимости от предоставляемых языком средств синтаксиса):
— класс-ориентированный подход (Simula, Smalltalk, PHP, C++, Java, C#, Objective-C, Perl, Ruby, Eiffel)
— агентно-ориентированный подход (распределённые объектные архитектуры (CORBA, DCOM, Java RMI, WEB-services), распределённая объектная среда JADE)
— компонентно-ориентированный подход (Oberon, Component Pascal, Java, платформа .NET, Rails Cells)
— прототипно-ориентированный подход (языки Self, JavaScript, Lua, Io, REBOL и др.)
Парадигма программирования не определяется однозначно языком программирования (практически все современные языки в той или иной мере допускают использование различных парадигм). Так, на языке Си, который не является объектно-ориентированным, можно работать в соответствии с принципами объектно-ориентированного программирования (хотя это и сопряжено с определёнными сложностями). Кому интересно:
Ben Klemens. 21st Century C (2nd Edition; см. глава 11. Object-Oriented Programming in C)
и даже
Axel-Tobias Schreiner. Object-Oriented Programming With ANSI-C
К сожалению, понятие классов и объектов меняется от языка к языку. На данный момент классическое ООП реализовано только в C++. Другие языки реализовали свою версию.