Программирование на C, C# и Java
Уроки программирования, алгоритмы, статьи, исходники, примеры программ и полезные советы
ОСТОРОЖНО МОШЕННИКИ! В последнее время в социальных сетях участились случаи предложения помощи в написании программ от лиц, прикрывающихся сайтом vscode.ru. Мы никогда не пишем первыми и не размещаем никакие материалы в посторонних группах ВК. Для связи с нами используйте исключительно эти контакты: vscoderu@yandex.ru, https://vk.com/vscode
Что такое класс в ООП
Класс — это ключевое понятие в объектно-ориентированном программировании. Не до конца понимая, что такое класс, невозможно успешно программировать в рамках парадигмы ООП. Данная статья посвящена понятию класса и его базовым элементам, таким как поля, методы и конструкторы.
Когда Вы переходите от процедурных языков (Си, Pascal, Basic) к объектно-ориентированным (C#, Java, C++), первое, что вам нужно сделать — это сломать своё мышление относительно того, что программирование — это написание функций и их последовательный вызов в некоторой главной (main). В рамках ООП вам придётся мыслить более абстрактно и работать с классами, которые являются воплощением объектов реального мира. Казалось бы, почему мыслить в рамках реальных объектов — это значит мыслить более абстрактно?
В программировании, которое изначально было процедурным, переход в объектам — это переход на один уровень абстракции выше. Парадоксально, но новичкам на первых порах довольно сложно перейти к мышлению в рамках реальных объектов при написании кода. Но освоив объектно-ориентированное программирование, вы, несомненно, останетесь довольны, потому что его мощь позволит вам создавать сложные программы значительно быстрее. Мы же со своей стороны постараемся на примерах доходчиво объяснить, что такое класс, и как его использовать. Приступим.
Что такое класс?
В объектно-ориентированном программировании (ООП) — класс это основной элемент, в рамках которого осуществляется конструирование программ. Класс содержит в себе данные и код, который управляет этими данными.
Класс зачастую описывает объект реального мира. Как и реальный объект, класс содержит свой набор параметров и характеристик. Каждый такой параметр называется поле класса (очень похоже на обычные переменные). Также класс способен манипулировать своими характеристиками (полями) с помощью методов класса (похожи на функции в процедурных языках). Рассмотрим такой объект, как автомобиль.
Оговоримся, что данная статья исключительно для начинающих. В ней не рассматривается наследование, абстрактные классы и т.д.
Урок №112. Введение в ООП
На уроке №10 мы определили объект в языке C++ как часть памяти, которая используется для хранения значений. Объект с именем называется переменной.
В традиционном программировании (чем мы занимались до этого момента), программа — это набор инструкций для компьютера, которые определяют данные (через объекты), а затем работают с этими данными (через операторы и функции). Объекты и функции, которые работают с этими данными, являются отдельными единицами, которые объединяются для получения программистом желаемого результата. Из-за того, что они являются отдельными единицами, традиционное программирование часто не позволяет использовать интуитивное представление реальности. Это является делом программиста — управлять и соединять свойства (переменные) с поведением (функциями) соответствующим образом, что приводит к созданию следующего кода:
Так что же тогда является объектно-ориентированным программированием? Для лучшего понимания воспользуемся аналогией. Оглянитесь вокруг, везде находятся объекты: книги, здания, еда и даже вы сами. Объекты имеют два основных компонента:
свойства (например, вес, цвет, размер, прочность, форма и т.д.);
поведение, которое они могут проявлять (например, открывать что-либо, делать что-то и т.д.).
Свойства и поведение неотделимы друг от друга.
Объектно-ориентированное программирование (сокр. «ООП») предоставляет возможность создавать объекты, которые объединяют свойства и поведение в самостоятельный союз, который затем можно многоразово использовать. Это приводит к созданию следующего кода:
Так не только читабельнее, но и понятнее, кем является объект ( you — вы) и какое поведение вызывается ( driveTo — поездка). Вместо того, чтобы сосредоточиться на написании функций, мы концентрируемся на определении объектов, которые имеют четкий набор поведений. Вот почему эта парадигма называется «объектно-ориентированной».
Это позволяет создавать программы модульным способом, что упрощает не только написание и понимание кода, но и обеспечивает более высокий уровень возможности повторного использования этого кода. Объекты также обеспечивают более интуитивный способ работы с данными, позволяя программисту определить, как он будет взаимодействовать с объектами, и как эти объекты будут взаимодействовать с другими объектами.
Обратите внимание, ООП не заменяет традиционные методы программирования. ООП — это дополнительный инструмент управления сложностью.
Объектно-ориентированное программирование также предоставляет несколько других полезных концепций, таких как наследование, инкапсуляция, абстракция и полиморфизм. Мы рассмотрим каждую из этих концепций на соответствующих уроках. Будет много нового материала, но как только вы разберетесь с ООП, вам уже не захочется возвращаться к традиционному программированию.
Обратите внимание, термин «объект» перегружен, он имеет несколько значений, что может вызывать некоторую путаницу. В традиционном программировании, «объект» — это часть памяти для хранения значений. В объектно-ориентированном программировании, «объект» — это тот же объект, что и в традиционном программировании, но который соединяет в себе как свойства, так и способы поведения. С этого момента мы будем использовать термин «объект» в объектно-ориентированном смысле этого слова.
Глава №7. Итоговый тест
Комментариев: 12
Еще раз спасибо за качественный перевод и оформление! У Вас прекрасный перевод, так все грамотно выглядит. Не могу нарадоваться, что нашла и подсела на Ваш сайт. Это лучшее, что я видела. Настолько все постепенно и по порядку, так грамотно составлены уроки, что, кажется, по другому невозможно все это объяснить и понять.
Дойдя до данной главы, сделала паузу для разнообразия на Страуструпа "Принципы и практика использования С++" — вот где каша и все в кучу((( Сначала покажут, через 5 страниц только объяснят. Это кошмар, хорошо, что я уже подготовлена Вашим сайтом. В начале книги совсем нет новой информации для меня (после Вашего сайта )
А здесь иногда происходит так: изучаешь урок, вроде все понятно, но мысль в голове — "к чему это? как это может использоваться?" — а в следующем или через 1-2 урока все встает на места и в новом материале все складывается. В этих уроках действительно все с нуля, подробно и без воды. Как вы откопали такое сокровище? Это просто диво дивное) Надеюсь, что у Вас получится перевести все уроки до конца!
alt=»Фото аватара» width=»50″ height=»50″ />Юрий :
Я иногда тоже удивляюсь, насколько всё последовательно. Текущая тема нужна для освоения последующей: уроки переплетаются не только между другими уроками текущей главы, но и между уроками соседних глав. Получается как в математике: если сегодня не разобрался с косинусами/синусами — завтра не разберешься с теоремами косинусов/синусов (как и в случае с теоремой Пифагора нужно знать, что такое катеты и гипотенуза).
Насчет книг. Материал один и тот же. Те же циклы, ООП, классы, типы данных. Но многое зависит от двух вещей: желание учащегося и способа изложения материала. Т.е. даже самый вкусный контент можно запороть унылой подачей, и также даже, казалось бы, сложные темы можно объяснить простыми словами. Конечно, не всё сразу, но потихоньку начинаешь углубляться и понимать.
Спасибо за ваш отзыв. Надеюсь хватит сил перевести уроки до конца
Здравствуйте. Очень признателен за эту вводящую в ооп статью. Но всё же мне непонятно,то есть не могу постичь смысла в этом виде программирования. Чтобы объяснить что я имею в виду, и объяснить где я не понимаю, я приведу примеры. Я заранее искренне надеюсь на вашу помощь.
Итак, для меня понятно,что существует некий код,который понимает компилятор, код, который что делает, выполняет какую-то функцию. Достаточно знать каков код нужен для реализации чего то конкретного и программа работает. Это мне понятно. Не понятно ООП. Не понятно как объект,который мы создаем( к примеру кнопка, при нажатии которой, отправляется сообщение кому то) может что то делать,выполнять? Откуда компьютер знает как ему что то сделать? Мы просто пишем название объекта, пишем на английском что он делает и это всё работает? К примеру, я создаю объект,которой называю "кнопка" на английском, пишу на английском что она нажимается, и потом на этом же английском пишу просто банально что она отправляет сообщение кому то и всё? Так работает ооп? Будет она работать? Спасибо ещё раз за помощь.
ООП и обработка нажатия кнопки соотносятся так же, как квадратное с зелёным: квадратное может быть зелёным, но это совсем не одно и то же.
Обработка нажатия кнопки — это взаимодействие с операционной системой по средствам обработки сообщений от неё (от Windows).
ООП — это философия написания программ. То есть, даже не язык программирования, а один из способов писать на нём.
То есть, мухи отдельно, котлеты отдельно. В прочем, программа, призванная обработать сообщения Windows, может быть написана с и с использованием приёмов ООП.
Напишу скорее для себя. Сейчас 99.9% всего кода написанного в мире, написано именно через концепцию ООП. Сомневаюсь что вы(или кто то другой), будет писать банальную обработку нажатия кнопки(и саму кнопку) в коммерческом проекте(для уже существующих ОС) через ассемблер. Это будет банальная трата личного времени, потому как там придется писать чертову уйму кода, когда как через ООП это делается в пару строчек.
Владимир, если речь идет о 99,9% коммерческих программ для использования на ПК, то вы вероятнее всего правы. Однако вы упускаете, что на один персональный компьютер приходятся десятки, если не сотни тысяч встраиваемых систем — от умных пультов и микроволновок до самолетов и спутников. В одних современных автомобилях находятся десятки контроллеров, выполняющих самые разнообразные задания, будь то обработка сигналов сенсоров или управление медиасистемой. Согласитесь, использовать для этих целей стандартное для ПК железо на винде или даже ПКшном линуксе — далеко не самая лучшая идея как в плане эффективности, так и в плане стоимости хардвирных компонентов.
Контроллеры же используемые во встраиваемых системах лишь в редких случаях имееют собственную операционную систему. Некоторые "мощные" 32-битные контроллеры используют особые версии линукс для встраиваемых систем, однако подавляющее большинство 8- и 16- битных контроллеров такой роскоши не имеют. А так как для подобных систем зачастую критически важна экономия памяти и скорость исполнения, то зачастую приходится отказываться от С++ со всеми прелестями ООП и возвращаться к дедушке С с его парадигмами структурного программирования.
Ассемблером так же не брезгуют. Продолжу ваш пример с кнопкой: представьте что речь идет не о клавиши клавиатуры или кнопке включения чайника, а о кнопке аварийной остановке станка с ЧПУ, в который угодил невнимательный коллега-рабочий. В таком случае каждая микросекунда будет на счету. Поэтому во всех коммерческих проектах "банальную обработку нажатия кнопки" пишут через ассемблер, не доверяя компилятору.
По этим причинам ваше высказывание о процентном соотношении кода написанного с ООП кажется неправдоподобным, или как минимум неполным, если вы имелли в виду исключительно программные проекты для ПК.
Я обычно не оставляю комментарии, но данное изложение реально одно из самых лучших.
Так как я только изучаю программирование и плюсы мой первый язык, то уроки Дениса Маркова + данный учебник по C++, наверное, лучшая комбинация в мире))
Объектно-ориентированное программирование на C++ в примерах
Мир состоит из объектов и сам — объект. Исследуя Мир, принято объекты классифицировать по каким-либо признакам. Выделяют классы животных и растений, планет и звезд, атомов и молекул, машин и механизмов, и многие, многие другие. Эта идея положена в основу объектно-ориентированного программирования.
Объектно-ориентированное программирование(ООП) подразумевает описание информационной модели взаимодействия объектов, которые могут содержать данные и алгоритмы. Данные представляются как поля (свойства, атрибуты), а алгоритмы — как процедуры или функции (методы).
Многие из используемых языков программирования (C++, Java, Python, Ruby и т. д.) в большей или меньшей степени поддерживают объектно-ориентированное программирование. Наиболее полно концепция ООП представлена в языке C++. Поэтому на нем написаны все примеры, приведенные ниже.
Классы
Любой объект принадлежит определенному классу. С точки зрения языка C++ класс — это тип, объект — это переменная.
Описать класс на языке C++ можно так:
Например, класс обыкновенных дробей может быть описан так:
В секции public: объявляются свойства и методы доступные в самом классе, его потомках. Кроме того, можно обратиться к ним через объект класса, используя точечную запись: имяОбъекта.свойтво/метод. Объявление конструкторов, деструктора, переопределение операций должно располагаться в этой секции.В приведенном примере определены:
CFract()<>; — конструктор без параметров;
CFract(int, int); — конструктор с двумя параметрами: целые числа;
CFract(const CFract&); — копирующий конструктор.
две операции присваивания(перегрузка операций — тема отдельной статьи):
CFract operator*(const CFract&);
CFract operator/(const CFract&);
CFract operator+(const CFract&);
CFract operator-(const CFract&);
Обратите внимание, что при объявлении этих методов указаны только типы параметров. Имена понадобятся в определении методов.
Секция private: содержит свойства и методы доступные только в определении методов этого класса. В этой секции объявлены два поля: num(числитель) и den(знаменатель)
Секция protected: содержит свойства и методы доступные в классе и его потомках. В примере здесь объявлены вспомогательные функции.
Определение методов возможна внутри описания класса или после. Первый вариант неудобен для дальнейшей разработки и сопровождения класса. Поэтому будем определять методы после определения всего класса.
Конструктор без параметров определен внутри класса. там не предусмотрено ни каких действий.
Конструктор с параметрами:
Может случится, что будет передан знаменатель меньше или равный нулю. Эту неприятность нужно обработать. В случае если передано отрицательное число, то нужно поменять знак и у числителя и у знаменателя, если передан 0, то вывести сообщение об ошибке. Для этого в protected объявим метод int SetDen(int).
Тогда конструктор с параметрами примет вид:
Кроме метода проверки знаменателя нужен метод сокращения дроби Reduce. Добавим его объявление в секцию protected: , а определение выглядит так:
Для сокращения дроби нужно найти наибольший общий делитель числителя и знаменателя. Добавим метод Nod в секцию protected, а ее определение выглядит так:
Еще один метод, который необходим в дальнейшем. Он также используется в методе Nod — обмен значениями переменных Swap. Добавим его объявление в секцию protected и определим:
Следующий важный метод — операция присваивания:
Ключевое слово operator указывает на то, что это не просто метод, а операция, которую в дальнейшем можно использовать в выражениях. Например:
Хорошо бы перегрузить операцию присваивания в классе CFract, чтобы иметь возможность читать дробь из строки, то есть чтобы была допустима запись:
Примерная реализация этого метода (не совсем очевидна; пожалуй это тема отдельной статьи):
Определение арифметических операций
Наиболее сложная из всех — операция сложения. Нужно найти наименьший общий знаменатель, дополнительные множители для дробей. Затем сложить произведения числителей на соответствующие дополнительные множители. Примерное определение операции сложения:
Остальные операции определить гораздо проще. В приложении приведены определения этих операций.
И последнее в этой заметке: объявление и определение принято писать в различных файлах: fract1.h — объявление класса, fract1.cpp — определение методов. Вот эти файлы:
файл fract1.h
файл fract1.cpp
Функция main нужна только для тестирования. Вообще ее следует определять в отдельном файле.
Love Soft
Загрузки всякие
Связь
Содержание
Классы
Классы в С++ — это абстракция описывающая методы, свойства, ещё не существующих объектов. Объекты — конкретное представление абстракции, имеющее свои свойства и методы. Созданные объекты на основе одного класса называются экземплярами этого класса. Эти объекты могут иметь различное поведение, свойства, но все равно будут являться объектами одного класса.
В ООП существует три основных принципа построения классов:
Инкапсуляция — это свойство, позволяющее объединить в классе и данные, и методы, работающие с ними и скрыть детали реализации от пользователя.
Наследование — это свойство, позволяющее создать новый класс-потомок на основе уже существующего, при этом все характеристики класса родителя присваиваются классу-потомку.
Полиморфизм — свойство классов, позволяющее использовать объекты классов с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.
Объявление классов
При объявлении класса, не обязательно объявлять три спецификатора доступа, и не обязательно их объявлять в таком порядке.
Пример: простейший класс, в котором будет объявлена одна функция, печатающая сообщение.
Имя класса принято начинать с большой буквы, последующие слова в имени также должны начинаться с большой буквы.
Методы класса — это те же функции, только объявлены они внутри класса, поэтому всё что относится к функциям актуально и для методов классов. Объявление классов выполняется аналогично объявлению функций, то есть класс можно объявлять в отдельном файле или в главном файле.
Главное отличие класса от Си-структуры в том, что её членами (полями) являются не только переменные, но и функции. Этим класс похож на пространство имён (namespace). Действительно, класс сам себе образует пространство имён.
Функции, живущие внутри класса называются методами. Обращение к методам экземпляра класса происходит так же, как и обращение к полям-атрибутам — через точку.
Объекты пустого класса имеют ненулевой размер.
Инкапсуляция
Управление доступом к частям класса осуществляется при помощи трех квалификаторов:
public: — начало публичной, открытой части класса (интерфейса)
private: — начало приватной, внутренней, сокрытой от внешнего кода части класса
protected: — начало защищенной части класса, сокрытой от всего внешнего кода, кроме прямых наследников
Чтобы реализовать принцип икапсуляции в полной мере, все члены класса по умолчанию должны быть сокрытыми, а не публичными.
Методам данного класса доступны все, и скрытые, и публичные атрибуты и методы, так как они «свои».
Когда представление типа скрыто, необходимо дать пользователю средства для инициализации переменных этого типа. Для этого и нужны конструкторы
Конструктор выделяется среди всех прочих функций данного класса тем, что имеет такое же имя, как и сам класс
Объявление класса в отдельном файле
Предположим, необходимо воспользоваться классом CppStudio — разработанный ранее нами класс. Для этого необходимо подключить файл, в котором он объявлен. Подключение файлов выполняется с помощью препроцессорной директивы #include.
Появится новая проблема — так как в файле с классом уже есть функция main(), то при построении проекта компилятор выдаст ошибку: «В проекте найдено несколько main() — функций.» Именно поэтому класс необходимо объявлять в отдельном файле, чтобы его можно было неоднократно использовать.
имя файла выбираем как правило такое же как и имя класса.
В созданном заголовочном файле объявляем класс и, если необходимо, подключаем дополнительные заголовочные.
Отделение интерфейса от реализации
Интерфейс класса — конструкция, определяющая методы и свойства, предоставляемые классом. Реализация класса — это способ осуществления работоспособности класса. До этого мы не отделяли интерфейс класса от его реализации, то есть реализация методов осуществлялась внутри класса.
Интерфейс класса должен выглядеть так:
В интерфейсе класса остались объявленные переменные и прототипы методов класса.
содержимое файла реализации методов класса:
Методы класса объявляются точно так же как и функции, только перед именем метода необходимо написать имя класса и поставить унарную операцию разрешения области действия :: . Операция разрешения области действия привязывает метод, объявленный извне, к классу, имя которого совпадает с именем в объявлении метода.
осталось подключить заголовочный файл в исполняемом файле с main() функцией
set и get функции
Атрибуты объекта хранятся в переменных, объявленных внутри класса. Причём, объявление переменных должно выполняться со спецификатором доступа private. доступ к ним могут получить только методы класса, внешний доступ к элементам данных запрещён.
Поэтому принято объявлять в классах специальные методы — так называемые set и get функции, с помощью которых можно манипулировать элементами данных.
set-функции инициализируют элементы данных,
get-функции позволяют просмотреть значения элементов данных.
Указатель на объект
Операция взятия адреса также применима к объектам, как и к другим переменным. Создаются указатели на объекты так же, как и обычные указатели.
Для доступа к методам и атрибутам по указателю класса используется оператор →, который эквивалентен разыменованию, а затем обращению по точке. Если указатель плохой, при исполнении этой операции случится Segmentation fault.
Конструкторы и деструкторы
Конструктор — специальная функция, которая выполняет начальную инициализацию элементов данных, причём имя конструктора обязательно должно совпадать с именем класса.
Важным отличием конструктора от остальных функций является то, что он не возвращает значений, в том числе и void.
В любом классе должен быть конструктор, даже если явным образом конструктор не объявлен, то компилятор предоставляет конструктор по умолчанию, без параметров.
Конструктор вызывается при создании объекта, при этом фактические параметры, передаваемые в конструктор, перечисляются в круглых скобках через запятую.
Деструктор (от слова destruct — разрушать) — специальный метод класса, который служит для уничтожения элементов класса. Чаще всего его используют тогда, когда в конструкторе, при создании объекта класса, динамически был выделен участок памяти и необходимо эту память очистить, если эти значения уже не нужны для дальнейшей работы программы.
Конструктор и деструктор мы всегда объявляем в разделе public
у деструктора также нет типа данных для возвращаемого значения, к тому же деструктору нельзя передавать никаких параметров
имя класса и конструктора должно быть идентично;
имя деструктора идентично имени конструктора, но с приставкой
В классе допустимо создавать несколько конструкторов. Имена будут одинаковыми. Компилятор будет их различать по передаваемым параметрам (как при перегрузке функций). Если мы не передаем в конструктор параметры, он считается конструктором по умолчанию;
в классе может быть объявлен только один деструктор;
конструктор срабатывает сразу, при создании объектов класса, поэтому, явно вызывать конструктор не нужно
Деструктор срабатывает автоматически. Деструктор не имеет ни возвращаемого значения, ни параметров, так как он вызывается автоматически.
локальные переменные (в том числе и объекты) существуют только во время работы своей области видимости: функции и даже блока из фигурных скобок, например, тела цикла или просто блока из фигурных скобок. Поэтому деструктор автоматом вызывается при выходе из блока.
Вопрос. Что получится если сделать конструктор приватным?
Ответ. В этом случае конструктор без параметров также не будет создан автоматически. И если никаких публичных конструкторов нет, то экземпляр такого класса создать извне класса невозможно.
Тем не менее, такие классы на практике существуют, и их экземпляры создаются из их же методов или из дружественных функций.
Конструктор копирования
Конструктор копирования нужен нам для того, чтобы создавать «реальные» копии объектов класса, а не побитовую копию объекта.
Такую «реальную» копию объекта надо создавать в нескольких случаях:
Например, мы передаем объект в функцию в виде параметра. Функция будет работать не с самим переданным объектом, а с его побитовой копией. Допустим в конструкторе класса, при создании объекта, выделяется определенный объем памяти, а деструктор класса эту память освобождает. Указатель побитовой копии объекта будет хранить тот же адрес памяти, что и оригинальный объект. И, когда при завершении работы функции и уничтожении побитовой копии объекта, сработает деструктор, он освободит память, которая была занята объектом-оригиналом. В придачу, еще и при завершении работы программы, деструктор сработает повторно и попытается еще раз освободить этот объем памяти, что неизбежно приведет к ошибкам программы.
Конструктор копирования используется только в случае инициализации и не используется вместо явного присваивания (то есть там, где используется оператор присваивания).
существует конструктор копирования, работа которого заключается в том, чтобы создать реальную копию объекта со своей личной выделенной динамической памятью. Синтаксис конструктора копирования следующий:
попробуйте закомментировать конструктор копирования в определении класса и запустите программу. Вы увидите, что конструктор сработает только дважды, а деструктор отработает пять раз. И если бы он освобождал память — это неизбежно привело бы к ошибке программы.
Конструктор копирования используется для инициализации класса путем создания копии необходимого объекта. Оператор присваивания копированием (или «копирующее присваивание») используется для копирования одного класса в другой (существующий) класс.
По умолчанию автоматически предоставляется конструктор копирования и оператор присваивания копированием, если вы не предоставили их сами. Предоставляемые компилятором функции выполняют поверхностное копирование, что может вызывать проблемы у классов, которые работают с динамически выделенной памятью.
Одним из вариантов решения таких проблем является переопределение конструктора копирования и оператора присваивания копированием для выполнения глубокого копирования.
l-value и r-value
l-value — проще всего понимать, как функцию, объект или переменную (или выражение, результатом которого является функция, объект или переменная), которая имеет свой адрес памяти.
Изначально l-values были определены как «значения, которые должны находиться в левой части операции присваивания». Однако позже в язык С++ было добавлено ключевое слово const, и l-values были разделены на две подкатегории:
r-value — все остальное, что не является l-value. Это литералы (например, 5), временные значения (например, x + 1) и анонимные объекты (например, Fraction(7, 3)).
r-values имеют область видимости выражения (уничтожаются в конце выражения, в котором находятся) и им нельзя что-либо присвоить. Этот запрет на присваивание имеет смысл, так как присваивая значение мы вызываем в объекте побочные эффекты.
если бы мы присваивали какое-либо значение для r-value, r-value либо выходило бы из области видимости, прежде чем у нас была бы возможность использовать присвоенное значение в следующем выражении (что делает операцию присваивания бесполезной)
До версии C++11 существовал только один тип ссылок, его называли просто — «ссылка». В C++11 этот тип ссылки еще называют «ссылкой l-value». Ссылки l-value могут быть инициализированы только изменяемыми l-values.
Ссылки l-value на константные объекты могут быть инициализированы с помощью как l-values, так и r-values. Однако эти значения не могут быть изменены (константы не изменяют свои значения).
Ссылки l-value на константные объекты особенно полезны, так как позволяют передавать аргументы любого типа (l-value или r-value) в функцию без выполнения копирования аргумента.
В C++11 добавили новый тип ссылок — ссылки r-value — это ссылки, которые инициализируются только значениями r-values.
ссылка l-value создается с использованием одного амперсанда, ссылка r-value создается с использованием двойного амперсанда
Ссылки r-value не могут быть инициализированы значениями l-values.
Ссылки r-value имеют два полезных свойства:
дальше терпение закончилось:
Перегрузка оператора присваивания
конструктор копирования используется при инициализации новых объектов, тогда как оператор присваивания заменяет содержимое уже существующих объектов
Перегрузка оператора присваивания (=) выполняется через метод класса
метод должен правильно обрабатывать самоприсваивание
Причина, по которой операция = возвращает My_Array& вместо void , проста — для объединения присваиваний, как например:
Конструктор перемещения и оператор присваивания перемещением
В C++11 появился новый тип неконстантных ссылок, носящий название «ссылка на праводопустимое выражение» (англ. rvalue reference) [аналог в паскале параметра var P: Pointer] и обозначаемый как T&&, и новый вид конструкторов — конструкторы перемещения (англ. move constructors). Конструктор перемещения принимает на входе значение неконстантной ссылки на объект класса, и используется для передачи владения ресурсами этого объекта. Конструкторы перемещения были придуманы для решения проблемы потери эффективности, связанной с созданием временных объектов.
Когда вызываются конструктор перемещения и оператор присваивания перемещением? Тогда когда аргументом для создания или присваивания является r-value. Чаще всего этим r-value будет литерал или временное значение (временный объект).
Определение конструктора перемещения и оператора присваивания перемещением выполняется аналогично определению конструктора копирования и оператора присваивания копированием. Однако, в то время как функции с копированием принимают в качестве параметра константную ссылку l-value, функции с перемещением принимают в качестве параметра неконстантную ссылку r-value.
Вместо выполнения глубокого копирования исходного объекта в неявный объект, мы просто перемещаем (воруем) ресурсы исходного объекта. Под этим подразумевается поверхностное копирование указателя на исходный объект в неявный (временный) объект, а затем присваивание исходному указателю значения null (точнее nullptr) и в конце удаление неявного объекта.
Пример: оператор присваивания перемещением
Делегирующие конструкторы
Не редкость встретить класс с несколькими конструкторами, которые частично выполняют одно и то же, например:
Поскольку Часть кода X требуется обоим конструкторам, то она дублируется в каждом из конструкторов.
дублирование кода — это то, чего следует избегать
если ваш компилятор не совместим с C++11, и вы попытаетесь вызвать один конструктор внутри другого конструктора, то это скомпилируется, но будет работать не так, как вы ожидаете.
До C++11 явный вызов одного конструктора из другого приводит к созданию временного объекта, который затем инициализируется с помощью конструктора этого объекта и отбрасывается, оставляя исходный объект неизменным.
Использование отдельного метода. Конструкторам разрешено вызывать другие методы класса, которые не являются конструкторами. Лучшим решением будет создание отдельного метода (не конструктора), который будет выполнять общую инициализацию, и оба конструктора будут вызывать этот метод. Например:
Делегирующие конструкторы. Начиная с C++11, конструкторам разрешено вызывать другие конструкторы. Этот процесс называется делегированием конструкторов (или «цепочкой конструкторов»). Чтобы один конструктор вызывал другой, нужно просто сделать вызов этого конструктора в списке инициализации членов.
Убедитесь, что вы вызываете конструктор из списка инициализации членов, а не из тела конструктора.
конструктору, который вызывает другой конструктор, не разрешается выполнять какую-либо инициализацию членов класса. Поэтому конструкторы могут либо вызывать другие конструкторы, либо выполнять инициализацию, но не всё сразу.
Указатель this
при вызове методов через объект, не указываем с какими параметрами будут работать методы класса (приватные члены класса доступны и так).
При вызове методов, которые принадлежат классу, им неявно автоматически передается указатель this.
у нас есть возможность использовать указатель this явно.
Указатель this — это указатель на адрес объекта класса, при этом он является скрытым первым параметром любого метода класса (кроме статических методов), а типом указателя выступает имя класса.