Объективно ориентированное программирование c
Основы объектно-ориентированного программирования
C# — Руководство по C# — Основы объектно-ориентированного программирования
Все основанные на объектах языки (C#, Java, С++, Smalltalk, Visual Basic и т.п.) должны отвечать трем основным принципам объектно-ориентированного программирования (ООП), которые перечислены ниже:
Инкапсуляция
Как данный язык скрывает детали внутренней реализации объектов и предохраняет целостность данных?
Наследование
Как данный язык стимулирует многократное использование кода?
Полиморфизм
Как данный язык позволяет трактовать связанные объекты сходным образом?
Прежде чем погрузиться в синтаксические детали реализации каждого принципа, важно понять базовую роль каждого из них.
Роль инкапсуляции
Инкапсуляция — это механизм программирования, объединяющий вместе код и данные, которыми он манипулирует, исключая как вмешательство извне, так и неправильное использование данных. В объектно-ориентированном языке данные и код могут быть объединены в совершенно автономный черный ящик. Внутри такого ящика находятся все необходимые данные и код. Когда код и данные связываются вместе подобным образом, создается объект. Иными словами, объект — это элемент, поддерживающий инкапсуляцию.
Т.е. инкапсуляция представляет собой способности языка скрывать излишние детали реализации от пользователя объекта. Например, предположим, что используется класс по имени DatabaseReader, который имеет два главных метода: Open() и Close().
Фиктивный класс DatabaseReader инкапсулирует внутренние детали нахождения, загрузки, манипуляций и закрытия файла данных. Программистам нравится инкапсуляция, поскольку этот принцип ООП упрощает кодирование. Нет необходимости беспокоиться о многочисленных строках кода, которые работают «за кулисами», чтобы реализовать функционирование класса DatabaseReader. Все, что потребуется — это создать экземпляр и отправлять ему соответствующие сообщения (например, «открыть файл по имени AutoLot.mdf, расположенный на диске С:»).
С идеей инкапсуляции программной логики тесно связана идея защиты данных. В идеале данные состояния объекта должны быть специфицированы с использованием ключевого слова private (или, возможно, protected). Таким образом, внешний мир должен вежливо попросить, если захочет изменить или получить лежащее в основе значение. Это хороший принцип, поскольку общедоступные элементы данных можно легко повредить (даже нечаянно, а не преднамеренно).
Основной единицей инкапсуляции в C# является класс, который определяет форму объекта. Он описывает данные, а также код, который будет ими оперировать. В C# описание класса служит для построения объектов, которые являются экземплярами класса. Следовательно, класс, по существу, представляет собой ряд схематических описаний способа построения объекта.
Код и данные, составляющие вместе класс, называют членами. Данные, определяемые классом, называют полями, или переменными экземпляра. А код, оперирующий данными, содержится в функциях-членах, самым типичным представителем которых является метод. В C# метод служит в качестве аналога подпрограммы. (К числу других функций-членов относятся свойства, события и конструкторы.) Таким образом, методы класса содержат код, воздействующий на поля, определяемые этим классом.
Роль наследования
Следующий принцип ООП — наследование — касается способности языка позволять строить новые определения классов на основе определений существующих классов. По сути, наследование позволяет расширять поведение базового (или родительского) класса, наследуя основную функциональность в производном подклассе (также именуемом дочерним классом):
Т.е. наследование представляет собой процесс, в ходе которого один объект приобретает свойства другого объекта. Это очень важный процесс, поскольку он обеспечивает принцип иерархической классификации. Если вдуматься, то большая часть знаний поддается систематизации благодаря иерархической классификации по нисходящей.
Если не пользоваться иерархиями, то для каждого объекта пришлось бы явно определять все его свойства. А если воспользоваться наследованием, то достаточно определить лишь те свойства, которые делают объект особенным в его классе. Он может также наследовать общие свойства своего родителя. Следовательно, благодаря механизму наследования один объект становится отдельным экземпляром более общего класса.
Роль полиморфизма
Последний принцип ООП — полиморфизм. Он обозначает способность языка трактовать связанные объекты в сходной манере. В частности, этот принцип ООП позволяет базовому классу определять набор членов (формально называемый полиморфным интерфейсом), которые доступны всем наследникам. Полиморфный интерфейс класса конструируется с использованием любого количества виртуальных или абстрактных членов.
По сути, виртуальный член — это член базового класса, определяющий реализацию по умолчанию, которая может быть изменена (или, говоря более формально, переопределена) в производном классе. В отличие от него, абстрактный метод — это член базового класса, который не предусматривает реализации по умолчанию, а предлагает только сигнатуру. Когда класс наследуется от базового класса, определяющего абстрактный метод, этот метод обязательно должен быть переопределен в производном классе. В любом случае, когда производные классы переопределяют члены, определенные в базовом классе, они по существу переопределяют свою реакцию на один и тот же запрос.
Рассмотрим для примера стек, т.е. область памяти, функционирующую по принципу «последним пришел — первым обслужен». Допустим, что в программе требуются три разных типа стеков: один — для целых значений, другой — для значений с плавающей точкой, третий — для символьных значений. В данном примере алгоритм, реализующий все эти стеки, остается неизменным, несмотря на то, что в них сохраняются разнотипные данные. В языке, не являющемся объектно-ориентированным, для этой цели пришлось бы создать три разных набора стековых подпрограмм с разными именами. Но благодаря полиморфизму для реализации всех трех типов стеков в C# достаточно создать лишь один общий набор подпрограмм. Зная, как пользоваться одним стеком, вы сумеете воспользоваться и остальными.
В более общем смысле понятие полиморфизма нередко выражается следующим образом: «один интерфейс — множество методов«. Это означает, что для группы взаимосвязанных действий можно разработать общий интерфейс. Полиморфизм помогает упростить программу, позволяя использовать один и тот же интерфейс для описания общего класса действий. Выбрать конкретное действие (т.е. метод) в каждом отдельном случае — это задача компилятора. Программисту не нужно делать это самому. Ему достаточно запомнить и правильно использовать общий интерфейс.
Введение в ООП с примерами на C#. Часть первая. Все, что нужно знать о полиморфизме
Введение в ООП с примерами на C#. Часть первая. Все, что нужно знать о полиморфизме
- Переводы , 14 июля 2016 в 21:00
- Пётр Соковых
Я много писал на смежные темы, вроде концепции MVC, Entity Framework, паттерна «Репозиторий» и т.п. Моим приоритетом всегда было полное раскрытие темы, чтобы читателю не приходилось гуглить недостающие детали. Этот цикл статей опишет абсолютно все концепции ООП, которые могут интересовать начинающих разработчиков. Однако эта статья предназначена не только для тех, кто начинает свой путь в программировании: она написана и для опытных программистов, которым может потребоваться освежить свои знания.
Сразу скажу, далеко в теорию мы вдаваться не будем — нас интересуют специфичные вопросы. Где это будет нужно, я буду сопровождать повествование кодом на C#.
Что такое ООП и в чём его плюсы?
«ООП» значит «Объектно-Ориентированное Программирование». Это такой подход к написанию программ, который основывается на объектах, а не на функциях и процедурах. Эта модель ставит в центр внимания объекты, а не действия, данные, а не логику. Объект — реализация класса. Все реализации одного класса похожи друг на друга, но могут иметь разные параметры и значения. Объекты могут задействовать методы, специфичные для них.
ООП сильно упрощает процесс организации и создания структуры программы. Отдельные объекты, которые можно менять без воздействия на остальные части программы, упрощают также и внесение в программу изменений. Так как с течением времени программы становятся всё более крупными, а их поддержка всё более тяжёлой, эти два аспекта ООП становятся всё более актуальными.
Что за концепции ООП?
Сейчас коротко о принципах, которые мы позже рассмотрим в подробностях:
- Абстракция данных: подробности внутренней логики скрыты от конечного пользователя. Пользователю не нужно знать, как работают те или иные классы и методы, чтоб их использовать. Подходящим примером из реальной жизни будет велосипед — когда мы ездим на нём или меняем деталь, нам не нужно знать, как педаль приводит его в движение или как закреплена цепь.
- Наследование: самый популярный принцип ООП. Наследование делает возможным повторное использование кода — если какой-то класс уже имеет какую-то логику и функции, нам не нужно переписывать всё это заново для создания нового класса, мы можем просто включить старый класс в новый, целиком.
- Инкапсуляция: включение в класс объектов другого класса, вопросы доступа к ним, их видимости.
- Полиморфизм: «поли» значит «много», а «морфизм» — «изменение» или «вариативность», таким образом, «полиморфизм» — это свойство одних и тех же объектов и методов принимать разные формы.
- Обмен сообщениями: способность одних объектов вызывать методы других объектов, передавая им управление.
Ладно, тут мы коснулись большого количества теории, настало время действовать. Я надеюсь, это будет интересно.
Полиморфизм
В этой статье мы рассмотрим буквально все сценарии использования полиморфизма, использование параметров и разные возможные типы мышления во время написания кода.
Перегрузка методов
- Давайте создадим консольное приложение InheritanceAndPolymorphism и класс Overload.cs с тремя методами DisplayOverload с параметрами, как ниже:
В главном методе Program.cs теперь напишем следующее:
И теперь, когда мы это запустим, вывод будет следующим:
DisplayOverload 100
DisplayOverload method overloading
DisplayOverload method overloading100
Класс Overload содержит три метода, и все они называются DisplayOverload , они различаются только типами параметров. В C# (как и в большистве других языков) мы можем создавать методы с одинаковыми именами, но разными параметрами, это и называется «перегрузка методов». Это значит, что нам нет нужды запоминать кучу имён методов, которые совершают одинаковые действия с разными типами данных.
Что нужно запомнить: метод идентифицируется не только по имени, но и по его параметрам.
Если же мы запустим следующий код:
Мы получим ошибку компиляции:
Error: Type ‘InheritanceAndPolymorphism.Overload’ already defines a member called ‘DisplayOverload’ with the same parameter types
Здесь вы можете видеть две функции, которые различаются только по возвращаемому типу, и скомпилировать это нельзя.
Что нужно запомнить: метод не идентифицируется по возвращаемому типу, это не полиморфизм.
Если мы попробуем скомпилировать
…то у нас это не получится:
Error: Type ‘InheritanceAndPolymorphism.Overload’ already defines a member called ‘DisplayOverload’ with the same parameter types
Здесь присутствуют два метода, принимающих целое число в качестве аргумента, с той лишь разницей, что один из них помечен как статический.
Что нужно запомнить: модификаторы вроде static также не являются свойствами, идентифицирующими метод.
Если мы запустим нижеследующий код, в надежде, что теперь-то идентификаторы у методов будут разными:
То нас ждёт разочарование:
Error: Cannot define overloaded method ‘DisplayOverload’ because it differs from another method only on ref and out
Что нужно запомнить: на идентификатор метода оказывают влияние только его имя и параметры (их тип, количество). Модификаторы доступа не влияют. Двух методов с одинаковыми идентификаторами существовать не может.
Роль ключевого слова params в полиморфизме
Параметры могут быть четырёх разных видов:
- переданное значение;
- преданная ссылка;
- параметр для вывода;
- массив параметров.
С первыми тремя мы, вроде, разобрались, теперь подробнее взглянем на четвёртый.
- Если мы запустим следующий код:
То получим две ошибки:
Error1: The parameter name ‘a’ is a duplicate
Error2: A local variable named ‘a’ cannot be declared in this scope because it would give a different meaning to ‘a’, which is already used in a ‘parent or current’ scope to denote something else
Отсюда следуют вывод: имена параметров должны быть уникальны. Также не могут быть одинаковыми имя параметра метода и имя переменной, созданной в этом же методе.
- Теперь попробуем запустить следующий код:
Overload.cs
Program.cs
Мы получим следующий вывод:
Akhil
Akhil 1
Akhil 2
Akhil 3
Мы можем передавать одинаковые ссылочные параметры столько раз, сколько захотим. В методе Display строка name имеет значение «Akhil». Когда мы меняем значение x на «Akhil1», на самом деле мы меняем значение name , т.к. через параметр x передана ссылка именно на него. То же и с y — все эти три переменных ссылаются на одно место в памяти.
Overload.cs
Program.cs
Это даст нам такой вывод:
Akhil 100
Mittal 100
OOP 100
Akhil 200
Нам часто может потребоваться передать методу n параметров. В C# такую возможность предоставляет ключевое слово params .
Важно: это ключевое слово может быть применено только к последнему аргументу метода, так что метод ниже работать не будет:
- В случае DisplayOverload первый аргумент должен быть целым числом, а остальные — сколь угодно много строк или наоборот, ни одной.
200 100
300 100
100 200
Важно запомнить: C# достаточно умён, чтоб разделить обычные параметры и массив параметров, даже если они одного типа.
- Посмотрите на следующие два метода:
- Следует упомянуть, что последний аргумент не обязательно заполнять отдельными объектами, можно его использовать, будто это обычный аргумент, принимающий массив, то есть:
Разница между ними в том, что первый запустится, и такая синтаксическая конструкция будет подразумевать, что в метод будет передаваться n массивов строк. Вторая же выдаст ошибку:
Error: The parameter array must be a single dimensional array
Запомните: массив параметров должен быть одномерным.
Overload.cs
Program.cs
Вывод будет следующим:
Akhil 3
Ekta 3
Arsh 3
Однако такой код:
Уже вызовет ошибку:
Error: The best overloaded method match for ‘InheritanceAndPolymorphism.Overload.DisplayOverload(int, params string[])’ has some invalid arguments
Error:Argument 2: cannot convert from ‘string[]’ to ‘string’
Думаю, тут всё понятно — или, или. Смешивать передачу отдельными параметрами и одним массивом нельзя.
- Теперь рассмотрим поведение следующей программы:
Overload.cs
Program.cs
После её выполнения мы получим в консоли:
Это происходит из-за того, что при подобном синтаксисе массив передаётся по ссылке. Однако стоит отметить следующую особенность:
Результатом выполнения такого кода будет
Ведь из переданных параметров C# автоматически формирует новый, временный массив.
- Теперь поговорим о приоритете языка в выборе методов. Предположим, у нас есть такой код:
C# рассматривает методы с массивом параметров последними, так что во втором случае будет вызван метод, принимающий два целых числа. В первом и третьем случае будет вызван метод с params , так как ничего кроме него запустить невозможно. Таким образом, на выходе мы получим:
parameterArray
The two integers 200 300
parameterArray
- Теперь кое-что интересное. Как вы думаете, каким будет результат выполнения следующей программы?
Overload.cs
Program.cs
В консоли мы увидим:
System.Int32 System.String System.Double
System.Object[] System.Object[] System.Int32 System.String System.Double
То есть, в первом и в четвёртом случаях массив передаётся именно как массив, заменяя собой objectParamArray , а во втором и третьем случаях массив передаётся как единичный объект, из которого создаётся новый массив из одного элемента.
В заключение
В этой статье мы рассмотрели перегрузку методов, особенности компиляции, с ней связанные, и буквально все возможные случаи использования ключевого слова params . В следующей мы рассмотрим наследование. Напоследок ещё раз повторим основные пункты, которые нужно запомнить:
- Метод идентифицируется не только по имени, но и по его параметрам.
- Метод не идентифицируется по возвращаемому типу.
- Модификаторы вроде static также не являются свойствами, идентифицирующими метод.
- На идентификатор метода оказывают влияние только его имя и параметры (их тип, количество). Модификаторы доступа не влияют. Двух методов с одинаковыми идентификаторами существовать не может.
- Имена параметров должны быть уникальны. Также не могут быть одинаковыми имя параметра метода и имя переменной, созданной в этом же методе.
- Ключевое слово params может быть применено только к последнему аргументу метода.
- C# достаточно умён, чтоб разделить обычные параметры и массив параметров, даже если они одного типа.
- Массив параметров должен быть одномерным.
Урок №112. Введение в ООП
Обновл. 31 Дек 2019 |
В уроке №10 мы определили объект в C++ как часть памяти, которая используется для хранения значений. Объект с именем называется переменной.
В традиционном программировании (чем мы занимались до этого момента), программа — это набор инструкций для компьютера, которые определяют данные (через объекты), а затем работают с этими данными (через операторы и функции). Объекты и функции, которые работают с этими данными, являются отдельными единицами, которые объединяются для получения программистом желаемого результата. Из-за того, что они являются отдельными единицами, традиционное программирование часто не позволяет использовать интуитивное представление реальности. Это является делом программиста — управлять и соединять свойства (переменные) с поведением (функциями) соответствующим образом, что приводит к созданию следующего кода:
Так что же тогда является объектно-ориентированным программированием? Для лучшего понимания воспользуемся аналогией. Оглянитесь вокруг — везде находятся объекты: книги, здания, еда и даже Вы. Объекты имеют два основных компонента:
Свойства (например: вес, цвет, размер, прочность, форма и т.д.).
Поведение, которое они могут проявлять (например: открывать что-либо, делать что-то и т.д.).
Свойства и поведение неотделимы друг от друга.
Объектно-ориентированное программирование (сокр. «ООП») предоставляет возможность создавать объекты, которые объединяют свойства и поведение в самостоятельный союз, который затем можно многоразово использовать. Это приводит к созданию следующего кода:
Так не только читабельнее, но и понятнее, кем является объект ( you — Вы) и какое поведение вызывается ( driveTo — поездка). Вместо того, чтобы сосредоточиться на написании функций, мы концентрируемся на определении объектов, которые имеют чёткий набор поведений. Вот почему эта парадигма называется «объектно-ориентированной».
Это позволяет писать программы модульным способом, что упрощает не только написание и понимание кода, но и обеспечивает более высокую степень возможности повторного использования этого кода. Объекты также обеспечивают более интуитивный способ работы с данными, позволяя программисту определить, как он будет взаимодействовать с объектами, и как эти объекты будут взаимодействовать с другими объектами.
Обратите внимание, ООП не заменяет традиционные методы программирования. ООП — это дополнительный инструмент управления сложностью.
Объектно-ориентированное программирование также предоставляет несколько других полезных концепций: наследование, инкапсуляция, абстракция и полиморфизм. Мы рассмотрим каждую из этих концепций в следующих соответствующих уроках. Будет много нового материала, но как только вы разберётесь с ООП, вам уже не захочется возвращаться к традиционному программированию.
Обратите внимание, термин «объект» перегружен, он имеет несколько значений, что может вызывать некоторую путаницу. В традиционном программировании, «объект» — это часть памяти для хранения значений. В объектно-ориентированном программировании, «объект» — это тот же объект, что и в традиционном программировании, но который соединяет в себе как свойства, так и способы поведения. С этого момента, мы будем использовать термин «объект» в объектно-ориентированном смысле.
Курсы «C#.NET Developer»
Курсы «Java Developer»
Курсы «Frontend Developer»
Курсы «JavaScript Developer»
Курсы «Python Developer»
Курсы «Unity/Game Developer»
Поделиться в социальных сетях:
Глава №7. Итоговый тест
Комментариев: 17
«Объектно-ориентированное программирование (ООП) предоставляет возможность создавать объекты, которые соединяют свойства и поведения в самостоятельный союз, который затем можно многоразово использовать. Это приводит к созданию следующего кода: you.driveTo(work); «
( Это что универсальный код для всех случаев жизни ? )
» Так не только читабельнее, но и понятнее, кем является объект (you — вы) и какое поведение вызывается (driveTo — поездка). Вместо сосредоточения на написании функций, мы концентрируемся на определении объектов, которые имеют четкий набор поведений. Вот почему эта парадигма называется «объектно-ориентированной».
Эта фраза приведена вами в тексте . На каком языке она сформулирована ( для кого ?). И кто сможет понять что вы ей хотите сказать .
Прошу немножко по русски и для людей которые только осваивают само понятие ООП.
Еще раз спасибо за качественный перевод и оформление! У Вас прекрасный перевод, так все грамотно выглядит. Не могу нарадоваться, что нашла и подсела на Ваш сайт. Это лучшее, что я видела. Настолько все постепенно и по порядку, так грамотно составлены уроки, что, кажется, по другому невозможно все это объяснить и понять.
Дойдя до данной главы, сделала паузу для разнообразия на Страуструпа «Принципы и практика использования С++» — вот где каша и все в кучу((( Сначала покажут, через 5 страниц только объяснят. Это кошмар, хорошо, что я уже подготовлена Вашим сайтом. В начале книги совсем нет новой информации для меня (после Вашего сайта 😉 )
А здесь иногда происходит так: изучаешь урок, вроде все понятно, но мысль в голове — «к чему это? как это может использоваться?» — а в следующем или через 1-2 урока все встает на места и в новом материале все складывается. В этих уроках действительно все с нуля, подробно и без воды. Как вы откопали такое сокровище? Это просто диво дивное) Надеюсь, что у Вас получится перевести все уроки до конца!
Я иногда тоже удивляюсь, насколько всё последовательно. Текущая тема нужна для освоения последующей: уроки переплетаются не только между другими уроками текущей главы, но и между уроками соседних глав. Получается как в математике: если сегодня не разобрался с косинусами/синусами — завтра не разберешься с теоремами косинусов/синусов (как и в случае с теоремой Пифагора нужно знать, что такое катеты и гипотенуза).
Насчет книг. Материал один и тот же. Те же циклы, ООП, классы, типы данных. Но многое зависит от двух вещей: желание учащегося и способа изложения материала. Т.е. даже самый вкусный контент можно запороть унылой подачей, и также даже, казалось бы, сложные темы можно объяснить простыми словами. Конечно, не всё сразу, но потихоньку начинаешь углубляться и понимать.
Спасибо за ваш отзыв. Надеюсь хватит сил перевести уроки до конца 🙂
Этот урок по ООП создают ваши умельцы — которые или не хотят , или не могут донести ученикам то для чего нужен Class . Рассказывая об ООП нужно раскрыть не только содержимое ( привести программный код в примерах ), а объяснить людям механизм для чего нужен Class и как его использовать.
Из аналогии обычного программирования нужно сказать , что Class это то место где собираются подпрограммы — функции относящиеся к одной тематике — объекту, например Class Array — массивы данных ( подпрограммы обработки данных в массивах — вычисление среднего , сумм элементов, сортировки и т.п. ) .
Переменные в private: это фактически глобальные переменные которые объявляются для всех подпрограмм в классе ( они видны из любой подпрограммы данного класса ) — зти переменные в private являются параметрами класса .
Для того чтобы их настроить ( занести данные в эти переменные при конкретном обращении ) используетcя SET функция или функция с любым другим именем , которая заносит в переменные указанные в private конкретные значения – инициализирует параметры для одного конкретного объекта ( экземпляра класса).
Функционал get функций это обращение к каким либо подпрограммам обработчикам , которые производят любые вычисления и любые операции с использование данных в переменных private и возвращают результаты .
Так как данные в private видны из любой подпрограммы в данном классе Class , эти данные не нужно указывать каждый раз в параметрах при обращении к подпрограммам обработчикам , они уже имеются в параметрах – private.
За счет этого уменьшается объем кода программы.
Вот как то так , я бы сказал в видео по началу обучению ООП.
Сомневаюсь, что ваше объяснение будет понятным хотя бы для 30% новичков. То, что вы вместили здесь — детальне расписывается в каждом отдельном уроке этой главы.
Посмотрим будет ли мое объяснеие более понятным чем ваше . Может кто нибудь прокомментирует.
Добрый день, Владимир. Я программирую на С++ уже порядком 10 лет. Не принимайте близко к сердцу, но ваше объяснение слишком сумбурно и готов поспорить, что ни один новичок вообще ничего не поймет из сказанного.
Владимир Сергеевич, все вы правильно написали, но начинающему просто непонятно, конкретика будет в соответствующих уроках. Я сам недавно окончил 4-х месячные курсы С++(очень формальные, если бы не ravesli.com не знаю, что и делал бы). Можно было бы еще и наследование, композицию, агрегирование классов вспомнить, дружественные классы, шаблоны классов. Или вспомнить, что классы — модификация структур, пришедших из С и начать их сравнивать. Но сразу это не поймешь, требуется постепенность. В этих уроках есть постепенный переход от простого к более сложному. Поверьте, что когда вам надо делать лабораторную, а теорию вы получите только в следующем модуле — это не есть хорошо и не способствует усвояемости материала.
работаю программистом 2 года, пытаюсь учить своего младшего брата программированию по этим урокам, увидел ваш комментарий, для эксперимента сначала дал прочитать сам материал, потом ваш комментарий, он ничего не понял из того что вы написали, ваш коментарий это как «С++ за час»
Здравствуйте. Очень признателен за эту вводящую в ооп статью. Но всё же мне непонятно,то есть не могу постичь смысла в этом виде программирования. Чтобы объяснить что я имею в виду, и объяснить где я не понимаю, я приведу примеры. Я заранее искренне надеюсь на вашу помощь.
Итак, для меня понятно,что существует некий код,который понимает компилятор, код, который что делает, выполняет какую-то функцию. Достаточно знать каков код нужен для реализации чего то конкретного и программа работает. Это мне понятно. Не понятно ООП. Не понятно как объект,который мы создаем( к примеру кнопка, при нажатии которой, отправляется сообщение кому то) может что то делать,выполнять? Откуда компьютер знает как ему что то сделать? Мы просто пишем название объекта, пишем на английском что он делает и это всё работает? К примеру, я создаю объект,которой называю «кнопка» на английском, пишу на английском что она нажимается, и потом на этом же английском пишу просто банально что она отправляет сообщение кому то и всё? Так работает ооп? Будет она работать? Спасибо ещё раз за помощь.
ООП и обработка нажатия кнопки соотносятся так же, как квадратное с зелёным: квадратное может быть зелёным, но это совсем не одно и то же.
Обработка нажатия кнопки — это взаимодействие с операционной системой по средствам обработки сообщений от неё (от Windows).
ООП — это философия написания программ. То есть, даже не язык программирования, а один из способов писать на нём.
То есть, мухи отдельно, котлеты отдельно. В прочем, программа, призванная обработать сообщения Windows, может быть написана с и с использованием приёмов ООП.
Я обычно не оставляю комментарии, но данное изложение реально одно из самых лучших.
Так как я только изучаю программирование и плюсы мой первый язык, то уроки Дениса Маркова + данный учебник по C++, наверное, лучшая комбинация в мире))
Спасибо и Вам за комментарий и что читаете 🙂
Спасибо Вам огромное за грамотные уроки!
Это лучший русскоязычный материал по плюсам с их нюансами из всего, что я находил.
Значит, будем продолжать 🙂
Спасибо большое от меня тоже) Я программирую в основном на си, нашёл много чего не знал на вашем сайте) Жду новых статей)
Спасибо, что читаете 🙂 Буду работать далее.
Классы и объекты
В данном уроке мы рассмотрим классы в C++ и познакомимся с объектно-ориентированным программированием. Объектно-ориентированное программирование или ООП — это одна из парадигм программирования. Парадигма — это, другими словами, стиль. Парадигма определяет какие средства используются при написании программы. В ООП используются классы и объекты. Все наши предыдущие программы имели элементы разных парадигм: императивной, процедурной, структурной.
Мы можем написать одинаковую программу в разных парадигмах. Парадигмы не имеют чёткого определения и часто пересекаются.
Давайте посмотрим на пример. Допустим, в нашей игре есть танки и они могут стрелять, при стрельбе у них уменьшается боезапас. Как мы можем это смоделировать без ООП:
У нас есть структура, которая содержит поле, представляющее количество снарядов, и есть функция атаки, в которую мы передаём танк. Внутри функции мы меняем количество снарядов. Так может выглядеть игра на языке C: структуры отдельно от функций, которые совершают действия со структурными переменными. Данную ситуацию можно смоделировать по-другому с помощью объектно-ориентированного программирования (Object-Oriented Programming, OOP) — ООП.В ООП действия привязываются к объектам.
Определение классов в C++
Класс — это пользовательский тип данных (также как и структуры). Т.е. тип данных, который вы создаёте сами. Для этого вы пишете определение класса. Определение класса состоит из заголовка и тела. В заголовке ставится ключевое слов class, затем имя класса (стандартный идентификатор C++). Тело помещается в фигурные скобки. В C++ классы и структуры почти идентичны. В языке C в структурах можно хранить только данные, но в C++ в них можно добавить действия.
В C++ ключевые слова struct и class очень близки и могут использоваться взаимозаменяемо. У них есть только одно отличие (об этом ниже). Вот как можно определить такой же класс с помощью struct:
Отличие только первом ключевом слове. В одном из прошлых уроков мы уже обсуждали структуры. что мы видим новое? Ключевые слова private и public — это спецификаторы доступа. Также мы видим, что внутри класса мы можем вставлять определения функций.
Определение класса это чертёж. Оно говорит нам из каких данных состоит класс и какие действия он может совершать. т.е. происходит объединение данных и действий в одной сущности.
Переменные и методы класса
Класс состоит из членов класса (class members). Члены класса могут быть переменными (data members) или методами (function members или methods). Переменные класса могут иметь любой тип данных (включая другие структуры и классы). Методы — это действия, которые может выполнять класс. По сути, это обычные функции.
Все методы класса имеют доступ к переменным класса. Обратите внимание, как мы обращаемся к ammo в методе Attack.
Создание объектов класса
Теперь у нас есть свой тип данных и мы можем создавать переменные данного типа. Если после определения структур мы могли создавать структурные переменные, то в случае классов, мы создаём объекты классов (или экземпляры). Разница между классами и структурами только в терминах. Для C++ это почти одно и то же.
Вот так мы можем создать объекты класса Tank и вызвать метод Attack:
t1 и t2 — объекты класса Tank. Для C++ объект класса — это всего-лишь переменная. Тип данных этих переменных — Tank. Ещё раз повторю, что классы (и структуры) позволяют создавать пользовательские типы данных.
В англоязычной литературе создание объектов классов также называется созданием экземпляров — instantiating.
Мы обращаемся к переменным класса и методам с помощью оператора точки (прямой доступ), также как мы обращались к полям структурных переменных.
В нашем примере каждый объект имеет доступ к своей копии ammo. ammo — переменная класса (data member). Attack — метод класса. У каждого объекта своя копия переменных класса, но все объекты одного класса вызывают одни и те же методы.
Размер объекта включает все данные, но не методы
В памяти переменные класса располагаются последовательно. Благодаря этому мы можем создавать массивы объектов и копировать их все вместе (если в классе этих объектов нет динамического выделения памяти). Это будет важно для нас, когда мы начнём работать с графикой в DirectX/OpenGL. Размер объекта класса можно узнать с помощью функции sizeof. При этом в качестве аргумента можно использовать как объект, так и сам класс:
Методы — это все лишь функции. Но в отличии от простых функций, у всех методов есть один скрытый параметр — указатель на объект, который вызывает данный метод. Именно благодаря этому указателю метод знает, какой объект вызвал его и какому объекту принадлежат переменные класса. Внутри метода имя этого указателя — this.
Указатель this
Вот как для компилятора выглядит любой метод:
Это просто иллюстрация. В реальности не нужно указывать аргумент (всё что в круглых скобках). Мы автоматически получаем доступ к указателю this. В данном случае его использование перед ammo необязательно, компилятор автоматически привяжет эту переменную к this.
Указатель this нужен, когда методу необходимо вернуть указатель на текущий объект.
Указатели на объекты
При работе с объектам в C++ вам неизбежно придётся работать с указателями (и ссылками). Как мы помним, при передаче в функцию по значению создаётся копия переменной. Если у вас сложный класс, содержащий большой массив или указатели, то копирование такого объекта может потребовать ненужное выделение дополнительной памяти или может быть вообще невозможным, в случае если в классе вы динамически выделяете память. Поэтому очень часто объекты создаются динамически. Для доступа к таким объектам используется оператор непрямого доступа (стрелочка):
При использовании ссылки на объект, для доступа к его членам используется оператор прямого доступа (точка), т.е. с ссылкой можно обращаться как с обычным объектом:
Чуть ниже мы увидим один случай, когда не обойтись без ссылок.
Конструктор класса (Constructor)
Конструктор класса — метод, вызываемый автоматически при создании объекта. Он используется для инициализации переменных класса и выделении памяти, если это нужно. По сути это обычный метод. Имя обязательно должно совпадать с именем класса и он не имеет возвращаемого значения. Рассмотрим новый класс:
Здесь, в конструкторе задаются начальные значения переменных, но мы можем делать в нём всё что угодно, это обычная функция.
Перегрузка конструктора класса
Перегрузка (overloading) конструктора позволяет создать несколько конструкторов для одного класса с разными параметрами. Всё то же самое, что и при перегрузке функций:
Начальные значения можно задавать в виде списка инициализации. Выше в конструкторе мы инициализировали переменные внутри тела. Список инициализации идёт перед телом конструктора и выглядит так:
В списке инициализации можно задать значение только части переменных класса.
Копирующий конструктор (Copy Constructor)
Без каких-либо действий с нашей стороны мы можем присваивать объектам другие объекты:
Здесь используется копирующий конструктор. Копирующий конструктор по умолчанию просто копирует все переменные класса в другой объект. Если в классе используется динамическое выделение памяти, то копирующий конструктор по умолчанию не сможет правильно создать новый объект. В таком случае вы можете перегрузить копирующий конструктор:
В копирующем конструкторе всегда используются ссылки. Это обязательно. Параметр point — это объект, стоящий справа от оператора присваивания.
Деструктор класса
Деструктор класса — метод, вызываемый автоматически при уничтожении объекта. Это происходит, например, когда область видимости объекта заканчивается. Деструктор нужно писать явно, если в классе происходит выделение памяти. Соответственно, в деструкторе вам необходимо освободить все указатели.
Допустим в нашем танке есть экипаж, пусть это будет один объект типа Unit. При создании танка мы выделяем память под экипаж. В деструкторе нам нужно будет освободить память:
Имя деструктора совпадает с именем класса и перед ним ставится тильда
. Деструктор может быть только один.
Объектно-ориентированное программирование в C++ (ООП)
Теперь, когда мы представляем что такое классы и объекты, и умеем с ними работать, можно поговорить о объектно-ориентированном программировании. Сам по себе стиль ООП предполагает использование классов и объектов. Но помимо этого, у ООП есть ещё три характерные черты: инкапсуляция данных, наследование и полиморфизм.
Инкапсуляция данных — Encapsulation
Что означает слово Encapsulation? Корень — капсула. En — предлог в. Инкапсуляция — это буквально помещение в капсулу. Что помещается в капсулу? Данные и действия над ними: переменные и функции. Инкапсуляция — связывание данных и функций. Давайте ещё раз взглянем на класс Tank:
Собственно, здесь в класс Tank мы поместили переменную ammo и метод Attack. В методе Attack мы изменяем ammo. Это и есть инкапсуляция: члены класса (данные и методы) в одном месте.
В C++ есть ещё одно понятие, которое связано с инкапсуляцией — сокрытие данных. Сокрытие предполагает помещение данных (переменных класса) в область, в которой они не будут видимы в других частях программы. Для сокрытия используются спецификаторы доступа (access specifiers). Ключевые слова public и private и есть спецификаторы доступа. public говорит, что весь следующий блок будет видим за пределами определения класса. private говорит, что только методы класса имеют доступ к данным блока. Пример:
Здесь мы видим, что объект может получить доступ только к членам класса, находящимся в блоке public. При попытке обратиться к членам класса (и переменным, и методам) блока private, компилятор выдаст ошибку. При этом внутри любого метода класса мы можем обращаться к членам блока private. В методе Move мы изменяем скрытые переменные x и y.
Хороший стиль программирования в ООП предполагает сокрытие всех данных. Как тогда задавать значения скрытых данных и получать доступ к ним? Для этого используются методы setters и getters.
Setters and Getters
Setters и Getters сложно красиво перевести на русский. В своих уроках я буду использовать английские обозначения для них. Setter (set — установить) — это метод, который устанавливает значение переменной класса. Getter (get — получить) — метод, который возвращает значение переменной:
Имена не обязательно должны включать Set и Get. Использование setters и getters приводит к увеличению количества кода. Можно ли обойтись без инкапсуляции и объявить все данные в блоке public? Да, можно. Но данная экономия кода имеет свои негативные последствия. Мы будем подробно обсуждать данный вопрос, когда будем говорить об интерфейсах.
Следующая концепция ООП — наследование.
Наследование (Inheritance) в C++
Производный класс не может получить доступ к private членам. Поэтому в классе Unit используется спецификатор protected. Данный спецификатор разрешает доступ к данным внутри класса и внутри дочерних классов, private же разрешает доступ только в методах самого класса.
При наследовании производный класс имеет доступ ко всем членам (public и protected) базового класса. Именно поэтому мы можем вызвать метод Move для объекта типа Archer.
Обратите внимание, как происходит наследование. При определении дочернего класса, после имени ставится двоеточие, слово public и имя базового класса. В следущем уроке мы рассмотрим для чего здесь нужно слово public.
Полиморфизм (Polymorphism)
Наследование открывает доступ к полиморфизму. Poly — много, morph — форма. Это очень мощная техника, которую мы будем использовать постоянно.
Полиморфизм позволяет поместить в массив разные типы данных:
Мы создали массив указателей на Unit. Но C++ позволяет поместить в такой указатель и указатель на любой дочерний классс. Данная техника будет особенно полезна, когда мы изучим виртуальные функции.
Заключение
Классы позволяют легко моделировать лубую предметную область. Иногда лучше избежать использование ООП, но об этом мы поговорим в другой раз.
В следующем уроке мы познакомимся с более сложными концепциями, касающимися классов: виртуалье методы, шаблоны, статичные члены. Впоследствии мы увидим, как классы используютя в DirectX.
Единственное отличие между классом и структурой в C++: по умолчанию в структуре используется спецификатор доступа public, а в классе — private. Часто в коде вы будете видеть, что структуры используются без методов, чисто для описания каких-либо сущностей. Но это делать необязательно это всего лишь соглашение.
Объектно-ориентированное программирование
10.2 Классы и объекты в C++
Хотя C++ и не первая попытка создать объектно-ориентированную версию языка С, среди попыток такого рода он оказался наиболее успешным. Очевидно, одна из причин успешности — то, каким образом объектная парадигма была встроена в синтаксис языка.
Ранние версии C++ были созданы в начале 1980-х Бьёрном Страуструпом для собственных нужд (в качестве универсального языка программирования, удобного для компьютерного моделирования). В создаваемый язык были заложены следующие основные принципы:
- поддержка различных стилей программирования, включая процедурный и объектно-ориентированный подходы;
- предоставление программисту полной свободы выбора — в т. ч. реализовать программные решения, которые могут казаться концептуально неверными;
- сохранение обратной совместимости с языком С, чтобы программист мог использовать только те дополнительные возможности C++ , которые он сочтёт нужным, или не использовать их вовсе.
Эти принципы заслужили высокую оценку программистов-практиков. В результате на текущий момент C++ является одним из наиболее распространённых языков программирования — как системного, так и прикладного.
10.2.1 Реализация ООП в C++. Классы и структуры
Синтаксис описания класса во многом копирует синтаксис описания структуры. В простейшем случае класс описывается так:
Как и при объявлении структуры, имя_класса становится новым именем типа данных, которое можно использовать для объявления переменных (объектов класса) [5, 6]. Членами класса будут переменные и функции, объявленные внутри класса. Функции-члены класса называют методами этого класса, а переменныечлены класса называют свойствами класса.
В C++ понятия ООП используются следующим образом 5, 6]:
- «класс»: пользовательский тип данных, во многом аналогичный структуре;
- «объект класса» или «переменная-экземпляр класса»: переменная, в описании которой какой-то класс указан в качестве типа данных;
- «свойство» или «переменная-член класса»: переменная, объявленная внутри класса (как поле внутри структуры); на практике чаще говорят не о свойстве класса, а о свойстве объекта, так как для конкретных объектов переменные — члены класса обладают конкретными значениями и потому имеют конкретный смысл.
- «метод»: функция, объявленная внутри класса.
По умолчанию все функции и переменные, объявленные в классе, являются закрытыми, т. е. принадлежат закрытой секции класса. Это значит, что они доступны для обращения только изнутри членов этого класса и недоступны извне. Для объявления открытых членов класса используется ключевое слово public с двоеточием, обозначающее начало открытой секции класса. Все члены класса, объявленные после слова public , доступны для обращения как изнутри этого же класса, и для любой другой части программы, в которой доступен класс.
Открытых и закрытых секций в классе может быть несколько, и они могут произвольно чередоваться. При необходимости обозначить очередную закрытую секцию, её начало обозначается ключевым словом private .
Более того, структуры в C++ были существенно доработаны (по сравнению с классическим вариантом структур языка С). В C++ структура может иметь помимо переменных-членов (т. е. полей структуры) также и функции-члены, а ещё в структурах можно вводить открытые и закрытые секции. В сущности, структуры отличаются от классов двумя вещами:
- в структурах вместо ключевого слова class пишется ключевое слово struct ;
- в структурах по умолчанию все члены являются отрытыми (иначе перестали бы работать программы, написанные на С).
Рассмотрим в качестве примера объект, представляющий собой геометрический вектор в трехмерном пространстве. Для простоты ограничимся хранением в объекте трёх координат и функции, вычисляющей модуль вектора. С учётом различий между структурами и классами, приведённые ниже варианты аналогичны.