Как сделать поля в c

Обновлено: 04.07.2024

Классы и структуры — это, по сути, шаблоны, по которым можно создавать объекты. Каждый объект содержит данные и методы, манипулирующие этими данными.

Общая форма определения класса

При определении класса объявляются данные, которые он содержит, а также код, оперирующий этими данными. Если самые простые классы могут содержать только код или только данные, то большинство настоящих классов содержит и то и другое.

Данные-члены

Данные-члены — это те члены, которые содержат данные класса — поля, константы, события. Данные-члены могут быть статическими (static). Член класса является членом экземпляра, если только он не объявлен явно как static. Давайте рассмотрим виды этих данных:

Поля (field)

Это любые переменные, ассоциированные с классом.

Константы

Константы могут быть ассоциированы с классом тем же способом, что и переменные. Константа объявляется с помощью ключевого слова const. Если она объявлена как public, то в этом случае становится доступной извне класса.

События

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

Функции-члены

Функции-члены — это члены, которые обеспечивают некоторую функциональность для манипулирования данными класса. Они включают методы, свойства, конструкторы, финализаторы, операции и индексаторы:

Методы (method)

Это функции, ассоциированные с определенным классом. Как и данные-члены, по умолчанию они являются членами экземпляра. Они могут быть объявлены статическими с помощью модификатора static.

Свойства (property)

Конструкторы (constructor)

Это специальные функции, вызываемые автоматически при инициализации объекта. Их имена совпадают с именами классов, которым они принадлежат, и они не имеют типа возврата. Конструкторы полезны для инициализации полей класса.

Финализаторы (finalizer)

Вызываются, когда среда CLR определяет, что объект больше не нужен. Они имеют то же имя, что и класс, но с предшествующим символом тильды. Предсказать точно, когда будет вызван финализатор, невозможно.

Операции (operator)

Индексаторы (indexer)

Позволяют индексировать объекты таким же способом, как массив или коллекцию.

Класс создается с помощью ключевого слова class. Ниже приведена общая форма определения простого класса, содержащая только переменные экземпляра и методы:

Обратите внимание на то, что перед каждым объявлением переменной и метода указывается доступ. Это спецификатор доступа, например public, определяющий порядок доступа к данному члену класса. Члены класса могут быть как закрытыми (private) в пределах класса, так открытыми (public), т.е. более доступными. Спецификатор доступа определяет тип разрешенного доступа. Указывать спецификатор доступа не обязательно, но если он отсутствует, то объявляемый член считается закрытым в пределах класса. Члены с закрытым доступом могут использоваться только другими членами их класса.

Давайте разберем пример создания класса, описывающего характеристики пользователя:

В данном примере определяется новый пользовательский класс UserInfo, который содержит 4 поля и 1 метод, которые являются открытыми (т.е. содержат модификатор доступа public). В методе Main() создаются два экземпляра этого класса: myInfo и myGirlFriendInfo. Затем инициализируются поля данных экземпляров и вызывается метод writeInConsoleInfo().

Прежде чем двигаться дальше, рассмотрим следующий основополагающий принцип: у каждого объекта имеются свои копии переменных экземпляра, определенных в его классе. Следовательно, содержимое переменных в одном объекте может отличаться от их содержимого в другом объекте. Между обоими объектами не существует никакой связи, за исключением того факта, что они являются объектами одного и того же типа. Так, если имеются два объекта типа UserInfo, то у каждого из них своя копия переменных Name, Family, Age и Adress, а их содержимое в обоих объектах может отличаться:

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

Вариант 1:
Используйте абстрактное свойство и переопределите его в унаследованных классах. Это выигрывает от того, что оно принудительно (вы должны его переопределить), и оно чистое. Но кажется немного неправильным возвращать значение жесткого кода вместо того, чтобы инкапсулировать поле, и это несколько строк кода, а не просто. Я также должен объявить тело для "set", но это менее важно (и, вероятно, есть способ избежать того, о чем я не знаю).

Вариант 3
Я могу использовать защищенное поле и установить значение в конструкторе. Это кажется довольно аккуратным, но я полагаюсь на то, что конструктор всегда устанавливает это, а с несколькими перегруженными конструкторами всегда есть шанс, что какой-то путь кода не установит значение.

Эта статья даст базовое понимание терминов "класс", "метод", "наследование", "перегрузка метода"

Содержание

Методы

Методы - это функции, объявление которых размещено внутри определения класса или структуры. В список переменных, доступных для метода, неявно попадают все поля структуры или класса, в котором он объявлен. Другими словами, в список областей видимости метода попадает область видимости структуры.

Взгляните на пример:

Методу Vec2f::getLength доступны все символы (т.е. переменные, функции, типы данных), которые были объявлены в одной из трёх областей видимости. При наличии символов с одинаковыми идентификаторами один символ перекрывает другой, т.к. поиск происходит от внутренней области видимости к внешней.

Понять идею проще на схеме. В ней область видимости названа по-английски: scope.

Схема

Поднимаясь по схеме от внутренней области видимости к внешней, легко понять, какие имена символов доступны в методе getLength:

  1. локальная переменная “lengthSquare”
  2. поля Vec2f под именами “x” и “y”
  3. всё, что есть в глобальной области видимости

К слову, в других методах структуры Vec2f переменная “lengthSquare” будет недоступна, а поля “x” и “y” будут доступны.

Конструкторы

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

Посмотрите на простой пример. В нём есть проблема: и поля, и параметры конструктора названы одинаково. В результате в области видимости конструктора доступны только параметры, и своими именами они перекрывают поля!

Язык C++ предлагает два решения. Первый способ - использовать косвенное обращение к полям через привязанный к методу объект. Указатель на него доступен по ключевому слову this :

Второй путь считается более правильным: мы используем специальную возможность конструкторов - “списки инициализации конструктора” (англ. constructor initializer lists). Списки инициализации - это список, разделённый запятыми и начинающийся с “:”. Элемент списка инициализации выглядит как field(expression) , т.е. для каждого выбранного программистом поля можно указать выражение, инициализирующее его. Имя переменной является выражением. Поэтому мы инициализируем поле его параметром:

Объявление и определение методов

C++ требует, чтобы каждый метод структуры или класса был упомянут в определении этой структуры или класса. Но допускается писать лишь объявление метода, о определение размещать где-нибудь в другом месте:

Классы и структуры

В C++ есть ключевое слово class - это практически аналог ключевого слова struct . Оба ключевых слова объявляют тип данных, и разница между ними есть только на стыке наследования и инкапсуляции. Других различий class и struct не существует.

Основы инкапсуляции

В C++ можно блокировать доступ к полям извне, но сохранять доступ для методов. Для этого введены три области доступа

  1. public - символ в этой области доступен извне
  2. private - символ из этой области доступен лишь собственных в методах
  3. protected - используется редко, о нём можете прочитать в документации

Давайте сделаем поля типа Vec2f недоступными извне. Также мы заменим ключевое слово struct на class - это не меняет смысла программы, но считается хорошим тоном использовать struct только если все поля доступны публично.

Запомните несколько хороших правил:

  • Используйте struct, если все поля публичные и не зависят друг от друга; используйте class, если между полями должны соблюдаться закономерности (например, поле “площадь” круга должно быть)

Основы наследования

В C++ новый тип может наследовать все поля и методы другого типа. Для этого достаточно указать структуру или класс в списке базовых типов. Такой приём используется в SFML при объявлении классов фигур:

Что означает public перед именем базового типа? Во-первых внешний код может передать RectangleShape в функцию, принимающую ссылку на Shape, то есть возможен так называемы upcast от более низкого (и более конкретного) типа RectangleShape к более высокому (и более абстрактному) типу Shape:

Во-вторых из-за public наследования все унаследованные поля и методы сохраняют свой уровень доступ: приватные остаются приватными, публичные остаются публичными. А если бы мы наследовали Shape с ключевым словом private, то уровень доступа стал бы ниже: все методы и поля стали бы приватными:

Контроль уровня доступа полей и методов - хитрый механизм, пройдёт немало времени, прежде чем вы научитесь пользоваться им правильно. В начале просто старайтесь сделать правильный выбор между private и public. Скорее всего поля будут private, а конструктор и все методы будут public. Это позволяет сохранять инвариант класса, то есть держать поля объекта в согласованном состоянии независимо от того, какие методы вызывают извне.

Основы полиморфизма: виртуальные методы и их перегрузка

SFML использует ещё одну идиому C++: виртуальные методы. Ключевые слова virtual , final , override относятся именно к этой идиоме. Например, в SFML определяется класс Drawable, который обозначает “сущность, которую можно нарисовать”. Все рисуемые классы SFML, включая sf::Sprite , sf::RectangleShape , sf::Text , прямо или косвенно наследуются от sf::Drawable .

Зачем это надо? Дело в том, что метод draw класса RenderWindow принимает параметр типа Drawable . Тем не менее, этот метод успешно рисует любые типы объектов: спрайты, фигуры, тексты. Он не выполняет проверок - он просто настраивает состояние рисования (RenderStates) и вызывает метод draw у сущности, которая является Drawable .

Виртуальный метод вызывается косвенно: если класс Shape , унаследованный от Drawable , переопределил метод, а потом был передан как параметр типа Drawable , то вызов метода draw всё равно приведёт к вызову переопределённого метода Shape::draw , а не метода Drawable::draw ! С обычными (не виртуальными) методами такого не происходит: если бы мы убрали слово virtual из объявления draw , то вызов метода draw у параметра типа Drawable всегда приводил бы к вызову Drawable::draw , даже если реальный тип объекта, скрытого за этим параметром, совсем другой.

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

Другими словами, RenderWindow и RectangleShape не знают, что они работают друг с другом, но тем не менее каждый вызывает правильный метод другого класса!

Иллюстрация

Когда вы просто вызываете window.draw(shape) , повышение класса происходит дважды: сначала конкретный класс фигуры повышается до более ограниченного класса Drawable, затем конкретный класс RenderWindow повышается до абстрактного RenderTarget. Всё это не требует времени при выполнении: просто компилятор выполняет проверки типов данных ещё при компиляции, не более того.

Как унаследовать Drawable: практический пример

Мы создадим класс, который рисует флаг России. Он будет унаследован от Drawable, чтобы использовать для рисования обычный метод draw у объекта окна.

Теперь мы можем реализовать конструктор и метод draw. В конструкторе мы должны вычислить и установить позиции и размеры трёх полос на флаге, а в методе draw мы должны их последовательно нарисовать.

я создал поле,но я не могу обращаться к kr. Почему?

вот это kr. не находится ни в геттере, ни в сеттере, ни в методе, ни в конструкторе. Оно написано там, где его быть не должно, потому и не работает.

1 ответ 1

Потому что Вы объявили член класса, но не создали объект, на который этот член класса должен ссылаться:

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

Похожие

Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.

дизайн сайта / логотип © 2022 Stack Exchange Inc; материалы пользователей предоставляются на условиях лицензии cc by-sa. rev 2022.1.28.41306

Весь реальный мир состоит из объектов. Города состоят из районов, в каждом районе есть свои названия улиц, на каждой улице находятся жилые дома, которые также состоят из объектов.

Практически любой материальный предмет можно представить в виде совокупности объектов, из которых он состоит. Допустим, что нам нужно написать программу для учета успеваемости студентов. Можно представить группу студентов, как класс языка C++. Назовем его Students .

Основные понятия

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

У каждого студента есть имя — name и фамилия last_name . Также, у него есть промежуточные оценки за весь семестр. Эти оценки мы будем записывать в целочисленный массив из пяти элементов. После того, как все пять оценок будут проставлены, определим средний балл успеваемости студента за весь семестр — свойство average_ball .

Методы — это функции, которые могут выполнять какие-либо действия над данными (свойствами) класса. Добавим в наш класс функцию calculate_average_ball() , которая будет определять средний балл успеваемости ученика.

  • Методы класса — это его функции.
  • Свойства класса — его переменные.

Функция calculate_average_ball() просто делит сумму всех промежуточных оценок на их количество.

Модификаторы доступа public и private

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

Закрытые данные класса размещаются после модификатора доступа private . Если отсутствует модификатор public , то все функции и переменные, по умолчанию являются закрытыми (как в первом примере).

Обычно, приватными делают все свойства класса, а публичными — его методы. Все действия с закрытыми свойствами класса реализуются через его методы. Рассмотрим следующий код.

Мы не можем напрямую обращаться к закрытым данными класса. Работать с этими данными можно только посредством методов этого класса. В примере выше, мы используем функцию get_average_ball() для получения средней оценки студента, и set_average_ball() для выставления этой оценки.

Функция set_average_ball() принимает средний балл в качестве параметра и присваивает его значение закрытой переменной average_ball . Функция get_average_ball() просто возвращает значение этой переменной.

Программа учета успеваемости студентов

Создадим программу, которая будет заниматься учетом успеваемости студентов в группе. Создайте заголовочный файл students.h, в котором будет находиться класс Students .

Мы добавили в наш класс новые методы, а также сделали приватными все его свойства. Функция set_name() сохраняет имя студента в переменной name , а get_name() возвращает значение этой переменной. Принцип работы функций set_last_name() и get_last_name() аналогичен.

Функция set_scores() принимает массив с промежуточными оценками и сохраняет их в приватную переменную int scores[5] .

Теперь создайте файл main.cpp со следующим содержимым.

В самом начале программы создается объект класса Students . Дело в том, что сам класс является только описанием его объекта. Класс Students является описанием любого из студентов, у которого есть имя, фамилия и возможность получения оценок.

Объект класса Students характеризует конкретного студента. Если мы захотим выставить оценки всем ученикам в группе, то будем создавать новый объект для каждого из них. Использование классов очень хорошо подходит для описания объектов реального мира.

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

Введенные данные мы передаем set-функциям, которые присваивают их закрытым переменным класса. После того, как были введены промежуточные оценки, мы высчитываем средний балл на основе этих оценок, а затем сохраняем это значение в закрытом свойстве average_ball , с помощью функции set_average_ball() .

Скомпилируйте и запустите программу.

Отделение данных от логики

Вынесем реализацию всех методов класса в отдельный файл students.cpp.

А в заголовочном файле students.h оставим только прототипы этих методов.

Такой подход называется абстракцией данных — одного из фундаментальных принципов объектно-ориентированного программирования. К примеру, если кто-то другой захочет использовать наш класс в своем коде, ему не обязательно знать, как именно высчитывается средний балл. Он просто будет использовать функцию calculate_average_ball() из второго примера, не вникая в алгоритм ее работы.

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

В начале обучения мы говорили о пространствах имен (namespaces). Каждый класс в C++ использует свое пространство имен. Это сделано для того, чтобы избежать конфликтов при именовании переменных и функций. В файле students.cpp мы используем оператор принадлежности :: перед именем каждой функции. Это делается для того, чтобы указать компилятору, что эти функции принадлежат классу Students .

Создание объекта через указатель

При создании объекта, лучше не копировать память для него, а выделять ее в в куче с помощью указателя. И освобождать ее после того, как мы закончили работу с объектом. Реализуем это в нашей программе, немного изменив содержимое файла main.cpp.

Конструктор и деструктор класса

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

Деструктор класса вызывается при уничтожении объекта. Имя деструктора аналогично имени конструктора, только в начале ставится знак тильды ~ . Деструктор не имеет входных параметров.

Читайте также: