Создание объекта в c#. Классы. Объектно-ориентированное программирование. Переменные ссылочного типа и присваивание

Опции DDS карт


Опции текстур с прозрачностью (альфа-каналом)

DXT3 для иконок и книг. В графе Mip-map надо выбрать NO (формат НЕ используется в Skyrim)
DXT5 лучшее качество для вещей и предметов, в графе Mip-map нужно выбирать All

Опции текстур без прозрачности:

DXT1 (статик, броня, оружие и так далее, в графе мип-мап нужно выбирать all)


Разновидности карт

Диффузная карта dds

Под этим определением понимается собственно сама базовая текстура, несущая информацию о поверхности и цвете.

Карта нормалей, префикс _n

Normal map - сделанная фильтром маска, сообщающая движку о перепадах неровностей на поверхности предмета. Добавляет текстуре объем.
- Формат карты высот и карты свечения, как правило, совпадает с форматом базовой текстуры, ее мипмаппинг так же.
- Для иконок, карт и книг OBLIVION НЕ нужна карта высот.
Информация в карте высот о цветах и узорах не читается - только об освещенности. Эта карта - прозрачная маска с преломлениями и искажениями. Как линза поверх вашего рисунка. Это позволит вам на базе текстуры, сделав ее черно белой, устроить вручную любой перепад света перед фильтрацией, чтобы сделать вашу карту высот точнее, скрыть детали, которые не нужно отличать от нуля, и наоборот, подчеркнуть те места, где на обычной текстуре нет перепада, но он нужен.

Карта свечения, префикс _g:

Glow map - черно-белая текстура свечения с альфаканалом. Все, что светится - должно быть белым. Все, что не светится - черным.

Карта глянца/зеркальности, префикс _m:

Mirror map - карта, добавляющая поверхности глянец с эффектом отражения окружения. Черный цвет - матовая поверхность без бликов и отражений, белый - полный блеск. (аналогично альфа-каналу карты _n, употребляется, когда карты нормалей и карты высот имеют формат DXT1, а блеск надо регулировать, в основном, в Skyrim

Карта блеска, префикс _s:

Specular map - карта, отвечающая за матовость/блеск поверхности (практически не используется в Oblivion, распространена в Skyrim)

Работа с альфа-каналом

В диффузной карте цвет альфа-канала в градации от черного к белому (серые тона) означает прозрачность текстуры.
Черный: полная прозрачность (текстура в этом месте "отсутствует")
Белый: полная непрозрачность

В карте нормалей цвет канала в градации от черного к белому (серые тона) будет означать блеск поверхности. (В Скайриме вместо этого для текстур БЕЗ канала DXT1 делается карта _s)
Черный: полная матовость
Белый: полный блеск

Выполнение заливки канала в Photoshop

У вас должно быть активно окно channels (каналы).
Есть два способа выполнить нужную заливку канала или выделения в нем.
- Если канала еще нет, выделяем нужное место на текстуре, заходим в каналы, и внизу окошка жмем иконку Add Alpha (добавить альфа-канал)
- Если канал уже есть, заходим в него, выделяем нужный участок или весь канал, с активным инструментом Выделение правой кнопкой щелкаем по выделенному месту. Выбираем Выполнить заливку. В открывшемся меню выбираем Черный или Белый и выставляем проценты.

Назначение прозрачности модели в Nifscope

Правой кнопкой про объекту - Node - Attach Properties - NiAlphaProperty

Примеры


Стекло
- модель с разрешением использовать прозрачность и с текстурами dds и _n.dds
- альфа-каналы залиты соответвенно ~50% и 20% черного .
Прозрачность и блеск.

Кожа
- 100% белого канал dds
- 80% черного канал _n.dds
Непрозрачная и с легким отливом

Лицо
- 100% белого канал dds
- 90% черного канал _n.dds
Матовая плотная поверхность

Вырезание элемента
- 100% черного в границах элемента в канале alpha диффузной карты (основной текстуры)

Яркий блеск
- 20% черного в канале alpha карты нормалей _n.dds

Ошибки при использовании плагина NVIDIA DDS

Распространенная ошибка Too many channel означает, что вы не выполнили сведение. Зайдите в столбец слоев, кликните правой кнопой по любому из них и выберите в выпадающем меню "Выполнить сведение" (flatten image).
- Отказ в сохранении даже при сделанном сведении означает, что размер вашей текстуры имеет нечетное значение. Стороны текстур должны быть четными. В идеале, сторона текстуры должна быть кратна 4: 64х64, 128х128, 256х256, 516х516 и так далее.

Советы текстурщикам

Многие полагают, что им надо рисовать металл или тряпку руками и впадают в ступор. На деле, необходимо использовать кисти и фотоматериалы для базы - никто вас за это не поколотит. Будет особенно чудно, если для базы текстуры вы воспользуетесь собственным цифровым аппаратом. Если нет - воспользуйтесь теми текстурами, что можете скачать в сети и потом отделайте их.

Самое приятное, если вы сможете нарисовать текстуру сами. Для этого можно воспользоваться многочисленными уроками.

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

Текстуру стены, дороги, металла стоит подгранжевать. Что означает это пафосное выражение? Гранж это грязь, старение, любые признаки действия энтропии. Царапины и зазубринки, следы полировки на металле, дождевые потеки, мох и трещинки на стенах, поношенность одежек, характерные для реализма. Для этого нужно создать набор кистей или скачать их ибо кистей сотни.

Достичь многих эффектов можно с помощью стилей и экшенов фотошопа, применяя их выборочно к отдельным участкам. Так можно сделать золото и серебро, например, камни и множество других вещей. Паттерны (бесшовные текстуры) тоже вам очень помогут. Скачать это все в сети не проблема. Со временем у вас останутся самые нужные вещи. У меня около 300 кистей и 200 паттернов, а так же около 50 Гб текстур, 6000 градиентов, 1500 стилей... в общем - вооружите свой фотошоп. Включите мозги и руки - создавайте свои текстуры, кисти, стили, паттерны, делайте их неповторимыми.

Создание объектов

Для объявления объекта произвольного типа используется следующая конструкция:

<тип класса> имя переменной = new <тип класса>();

Например:

InfoUser myinfo = new InfoUser();

Эта строка объявления выполняет три функции. Во-первых, объявляется переменная myinfo, относящаяся к типу класса InfoUser. Сама эта переменная не является объектом, а лишь переменной, которая может ссылаться на объект. Во-вторых, создается конкретная, физическая, копия объекта. Это делается с помощью оператора new . И наконец, переменной myinfo присваивается ссылка на данный объект. Таким образом, после выполнения анализируемой строки объявленная переменная myinfo ссылается на объект типа InfoUser.

Оператор new динамически (т.е. во время выполнения) распределяет память для объекта и возвращает ссылку на него, которая затем сохраняется в переменной. Следовательно, в C# для объектов всех классов должна быть динамически распределена память.

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

Переменные ссылочного типа и присваивание

В операции присваивания переменные ссылочного типа действуют иначе, чем переменные типа значения, например типа int. Когда одна переменная типа значения присваивается другой, ситуация оказывается довольно простой. Переменная, находящаяся в левой части оператора присваивания, получает копию значения переменной, находящейся в правой части этого оператора.

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

В качестве примера рассмотрим следующий фрагмент кода:

Using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class autoCar { public string marka; } class Program { static void Main(string args) { autoCar Car1 = new autoCar(); autoCar Car2 = Car1; Car1.marka = "Renault"; Console.WriteLine(Car1.marka); Console.WriteLine(Car2.marka); Console.ReadLine(); } } }

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

Инициализаторы объектов

Инициализаторы объектов предоставляют способ создания объекта и инициализации его полей и свойств. Если используются инициализаторы объектов, то вместо обычного вызова конструктора класса указываются имена полей или свойств, инициализируемых первоначально задаваемым значением. Следовательно, синтаксис инициализатора объекта предоставляет альтернативу явному вызову конструктора класса. Синтаксис инициализатора объекта используется главным образом при создании анонимных типов в LINQ-выражениях. Но поскольку инициализаторы объектов можно, а иногда и нужно использовать в именованном классе, то ниже представлены основные положения об инициализации объектов.

Ниже приведена общая форма синтаксиса инициализации объектов:

new имя_класса {имя = выражение, имя = выражение, ...}

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

Последнее обновление: 27.07.2018

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

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

По умолчанию проект консольного приложения уже по умолчанию содержит один класс Program, с которого и начинается выполнение программы.

По сути класс представляет новый тип, который определяется пользователем. Класс определяется с помощью ключевого слова сlass :

Class Person { }

Где определяется класс? Класс можно определять внутри пространства имен, вне пространства имен, внутри другого класса. Как правило, классы помещаются в отдельные файлы. Но в данном случае поместим новый класс в файле, где располагается класс Program. То есть файл Program.cs будет выглядеть следующим образом:

Using System; namespace HelloApp { class Person { } class Program { static void Main(string args) { } } }

Вся функциональность класса представлена его членами - полями (полями называются переменные класса), свойствами, методами, событиями. Например, определим в классе Person поля и метод:

Using System; namespace HelloApp { class Person { public string name; // имя public int age; // возраст public void GetInfo() { Console.WriteLine($"Имя: {name} Возраст: {age}"); } } class Program { static void Main(string args) { Person tom; Console.ReadKey(); } } }

В данном случае класс Person представляет человека. Поле name хранит имя, а поле age - возраст человека. А метод GetInfo выводит все данные на консоль. Чтобы все данные были доступны вне класса Person переменные и метод определены с модификатором public.

Поскольку класс представляет собой новый тип, то в программе мы можем определять переменные, которые представляют данный тип. Так, здесь в методе Main определена переменная tom, которая представляет класс Person. Но пока эта переменная не указывает ни на какой объект и по умолчанию она имеет значение null . Поэтому вначале необходимо создать объект класса Person.

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

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

Конструктор по умолчанию

Если в классе не определено ни одного конструктора, то для этого класса автоматически создается конструктор по умолчанию. Такой конструктор не имеет парамтров и не имеет тела.

Выше класс Person не имеет никаких конструкторов. Поэтому для него автоматически создается конструктор по умолчанию. И мы можем использовать этот конструктор. В частности, создадим один объект класса Person:

Class Program { static void Main(string args) { Person tom = new Person(); tom.GetInfo(); // Имя: Возраст: 0 tom.name = "Tom"; tom.age = 34; tom.GetInfo(); // Имя: Tom Возраст: 34 Console.Read(); } }

Для создания объекта Person используется выражение new Person() . Оператор new выделяет память для объекта Person. И затем вызывается конструктор по умолчанию, который не принимает никаких параметров. В итоге после выполнения данного выражения в памяти будет выделен участок, где будут храниться все данные объекта Person. А переменная tom получит ссылку на созданный объект.

Если конструктор не инициализирует значения переменных объекта, то они получают значения по умолчанию. Для переменных числовых типов это число 0, а для типа string и классов - это значение null (то есть фактически отсутствие значения).

После создания объекта мы можем обратиться к переменным объекта Person через переменную tom и установить или получить их значения, например, tom.name = "Tom"; .

Имя: Возраст: 0 Имя: Tom Возраст: 34

Создание конструкторов

Выше для инициализации объекта использовался конструктор по умолчанию. Однако мы сами можем определить свои конструкторы:

Class Person { public string name; public int age; public Person() { name = "Неизвестно"; age = 18; } // 1 конструктор public Person(string n) { name = n; age = 18; } // 2 конструктор public Person(string n, int a) { name = n; age = a; } // 3 конструктор public void GetInfo() { Console.WriteLine($"Имя: {name} Возраст: {age}"); } }

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

Static void Main(string args) { Person tom = new Person(); // вызов 1-ого конструктора без параметров Person bob = new Person("Bob"); //вызов 2-ого конструктора с одним параметром Person sam = new Person("Sam", 25); // вызов 3-его конструктора с двумя параметрами bob.GetInfo(); // Имя: Bob Возраст: 18 tom.GetInfo(); // Имя: Неизвестно Возраст: 18 sam.GetInfo(); // Имя: Sam Возраст: 25 Console.ReadKey(); }

Консольный вывод данной программы:

Имя: Неизвестно Возраст: 18 Имя: Bob Возраст: 18 Имя: Sam Возраст: 25

При этом если в классе определены конструкторы, то при создании объекта необходимо использовать один из этих конструкторов.

Ключевое слово this

Ключевое слово this представляет ссылку на текущий экземпляр класса. В каких ситуациях оно нам может пригодиться? В примере выше определены три конструктора. Все три конструктора выполняют однотипные действия - устанавливают значения полей name и age. Но этих повторяющихся действий могло быть больше. И мы можем не дублировать функциональность конструкторов, а просто обращаться из одного конструктора к другому через ключевое слово this, передавая нужные значения для параметров:

Class Person { public string name; public int age; public Person() : this("Неизвестно") { } public Person(string name) : this(name, 18) { } public Person(string name, int age) { this.name = name; this.age = age; } public void GetInfo() { Console.WriteLine($"Имя: {name} Возраст: {age}"); } }

В данном случае первый конструктор вызывает второй, а второй конструктор вызывает третий. По количеству и типу параметров компилятор узнает, какой именно конструктор вызывается. Например, во втором конструкторе:

Public Person(string name) : this(name, 18) { }

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

Также стоит отметить, что в третьем конструкторе параметры называются также, как и поля класса.

Public Person(string name, int age) { this.name = name; this.age = age; }

И чтобы разграничить параметры и поля класса, к полям класса обращение идет через ключевое слово this. Так, в выражении this.name = name; первая часть this.name означает, что name - это поле текущего класса, а не название параметра name. Если бы у нас параметры и поля назывались по-разному, то использовать слово this было бы необязательно. Также через ключевое слово this можно обращаться к любому полю или методу.

Инициализаторы объектов

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

Person tom = new Person { name = "Tom", age=31 }; tom.GetInfo(); // Имя: Tom Возраст: 31

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

При использовании инициализаторов следует учитывать следующие моменты:

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

    Инициализатор выполняется после конструктора, поэтому если и в конструкторе, и в инициализаторе устанавливаются значения одних и тех же полей и свойств, то значения, устанавливаемые в конструкторе, заменяются значениями из инициализатора.

Классы и объекты в С++ являются основными концепциями объектно-ориентированного программирования — ООП. Объектно-ориентированное программирование — расширение структурного программирования, в котором основными концепциями являются понятия классов и объектов. Основное отличие языка программирования С++ от С состоит в том, что в С нет классов, а следовательно язык С не поддерживает ООП, в отличие от С++.

Чтобы понять, для чего же в действительности нужны классы, проведём аналогию с каким-нибудь объектом из повседневной жизни, например, с велосипедом. Велосипед — это объект, который был построен согласно чертежам. Так вот, эти самые чертежи играют роль классов в ООП. Таким образом классы — это некоторые описания, схемы, чертежи по которым создаются объекты. Теперь ясно, что для создания объекта в ООП необходимо сначала составить чертежи, то есть классы. Классы имеют свои функции, которые называются методами класса. Передвижение велосипеда осуществляется за счёт вращения педалей, если рассматривать велосипед с точки зрения ООП, то механизм вращения педалей — это метод класса. Каждый велосипед имеет свой цвет, вес, различные составляющие — всё это свойства. Причём у каждого созданного объекта свойства могут различаться. Имея один класс, можно создать неограниченно количество объектов (велосипедов), каждый из которых будет обладать одинаковым набором методов, при этом можно не задумываться о внутренней реализации механизма вращения педалей, колёс, срабатывания системы торможения, так как всё это уже будет определено в классе. Разобравшись с назначением класса, дадим ему грамотное определение.

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

  1. Инкапсуляция — это свойство, позволяющее объединить в классе и данные, и методы, работающие с ними и скрыть детали реализации от пользователя.
  2. Наследование — это свойство, позволяющее создать новый класс-потомок на основе уже существующего, при этом все характеристики класса родителя присваиваются классу-потомку.
  3. Полиморфизм — свойство классов, позволяющее использовать объекты классов с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.

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

// объявление классов в С++ class /*имя класса*/ { private: /* список свойств и методов для использования внутри класса */ public: /* список методов доступных другим функциям и объектам программы */ protected: /*список средств, доступных при наследовании*/ };

Объявление класса начинается с зарезервированного ключевого слова class ,послекоторого пишется имя класса. В фигурных скобочках, строки 3 — 10 объявляется тело класса, причём после закрывающейся скобочки обязательно нужно ставить точку с запятой, строка 10 . В теле класса объявляются три метки спецификации доступа, строки 4, 6, 8, после каждой метки нужно обязательно ставить двоеточие. В строке 4 объявлена метка спецификатора доступа private . Все методы и свойства класса, объявленные после спецификатор доступа private будут доступны только внутри класса. В строке 6 объявлен спецификатор доступа public , все методы и свойства класса, объявленные после спецификатора доступа public будут доступны другим функциям и объектам в программе. Пока на этом остановимся, спецификатор доступа protected разбирать сейчас не будем, просто запомните, что он есть. При объявлении класса, не обязательно объявлять три спецификатора доступа, и не обязательно их объявлять в таком порядке. Но лучше сразу определиться с порядком объявления спецификаторов доступа, и стараться его придерживаться. Разработаем программу, в которой объявим простейший класс, в котором будет объявлена одна функция, печатающая сообщение.

using namespace std; // начало объявления класса class CppStudio // имя класса { public: // спецификатор доступа void message() // функция (метод класса) выводящая сообщение на экран { cout << "website: сайт\ntheme: Classes and Objects in C + +\n"; } }; // конец объявления класса CppStudio int main(int argc, char* argv) { CppStudio objMessage; // объявление объекта objMessage.message(); // вызов функции класса message system("pause"); return 0; }

В строках 7 — 14 мы определили класс с именем CppStudio . Имя класса принято начинать с большой буквы, последующие слова в имени также должны начинаться с большой буквы. Такое сочетание букв называют верблюжьим регистром, так как чередование больших и маленьких букв напоминает силуэт верблюда. В теле класса объявлен спецификатор доступа public , который позволяет вызывать другим функциям методы класса, объявленные после public . Вот именно поэтому в главной функции, в строке 19 мы смогли вызвать функцию message() . В классе CppStudio объявлена всего одна функция, которая не имеет параметров и выводит сообщение на экран, строка 12 . Методы класса — это те же функции, только объявлены они внутри класса, поэтому всё что относится к функциям актуально и для методов классов. Объявление классов выполняется аналогично объявлению функций, то есть класс можно объявлять в отдельном файле или в главном файле, позже посмотрим как это делается. В строке 18 объявлена переменная objMessage типа CppStudio , так вот, переменная objMessage — это объект класса CppStudio . Таким образом, класс является сложным типом данных. После того как объект класса объявлен, можно воспользоваться его методами. Метод всего один — функция message() . Для этого обращаемся к методу объекта objMessage через точку, как показано в строке 19 , в результате программа выдаст текстовое сообщение (см. Рисунок 1).

Website: сайт theme: Classes and Objects in C + +

Рисунок 1 — Классы в С++

set — функции и get — функции классов

Каждый объект имеет какие-то свои свойства или атрибуты, которые характеризуют его на протяжении всей жизни. Атрибуты объекта хранятся в переменных, объявленных внутри класса, которому принадлежит данный объект. Причём, объявление переменных должно выполняться со спецификатором доступа private . Такие переменные называются элементами данных. Так как элементы данных объявлены в private , то и доступ к ним могут получить только методы класса, внешний доступ к элементам данных запрещён. Поэтому принято объявлять в классах специальные методы — так называемые set и get функции, с помощью которых можно манипулировать элементами данных. set-функции инициализируют элементы данных, get-функции позволяют просмотреть значения элементов данных. Доработаем класс CppStudio так, чтобы в нём можно было хранить дату в формате дд.мм.гг . Для изменения и просмотра даты реализуем соответственно set и get функции.

// classes.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include using namespace std; class CppStudio // имя класса { private: // спецификатор доступа private int day, // день month, // месяц year; // год public: // спецификатор доступа public void message() // функция (метод класса) выводящая сообщение на экран { cout << "\nwebsite: сайтntheme: Classes and Objects in C + +\n"; } void setDate(int date_day, int date_month, int date_year) // установка даты в формате дд.мм.гг { day = date_day; // инициализация день month = date_month; // инициализация месяц year = date_year; // инициализация год } void getDate() // отобразить текущую дату { cout << "Date: " << day << "." << month << "." << year << endl; } }; // конец объявления класса CppStudio int main(int argc, char* argv) { setlocale(LC_ALL, "rus"); // установка локали int day, month, year; cout << "Введите текущий день месяц и год!\n"; cout << "день: "; cin >> day; cout << "месяц: "; cin >> month; cout << "год: "; cin >> year; CppStudio objCppstudio; // объявление объекта objCppstudio.message(); // вызов функции класса message objCppstudio.setDate(day, month, year); // инициализация даты objCppstudio.getDate(); // отобразить дату system("pause"); return 0; }

В определении класса появился новый спецификатор доступа private , строка 9 . Данный спецификатор доступа ограничивает доступ к переменным, которые объявлены после него и до начала спецификатора доступа public , строки 9 — 12 . Таким образом к переменным day, month, year , могут получить доступ только методы класса. Функции не принадлежащие классу, не могут обращаться к этим переменным. Элементы данных или методы класса, объявленные после спецификатора доступа private , но до начала следующего спецификатора доступа называются закрытыми элементами данных и закрытыми методами класса. Следуя принципу наименьших привилегий и принципу хорошего программирования, целесообразно объявлять элементы данных после спецификатора доступа private , а методы класса — после спецификатора доступа public . Тогда, для манипулирования элементами данных, объявляются специальные функции — get и set . В класс CppStudio мы добавили два метода setDate() и getDate() , подробно рассмотрим каждый метод. Метод setDate() определён с 18 по 23 строки . Как уже ранее упоминалось, set — функции инициализируют элементы данных, поэтому метод setDate() выполняет именно такую функцию. То есть метод setDate() инициализирует переменные day, month, year . Чтобы просмотреть, значения в закрытых элементах данных объявлена функция getDate() ,которая возвращает значения из переменных day, month, year в виде даты.На этом определение класса закончено, в main(), как и всегда, создаем объект класса, и через объект вызываем его методы,строки 39 — 41 . Если бы элементы данных были объявлены после спецификатора public мы бы смогли к ним обратиться точно также, как и к методам класса. Результат работы программы показан на рисунке 2.

Введите текущий день месяц и год! день: 10 месяц: 11 год: 2011 website: сайтntheme: Classes and Objects in C + + Date: 10.11.2011

Рисунок 2 — Классы в С++

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

В предыдущей программе, у класса CppStudio были объявлены элементы данных, которые могут хранить информацию о дате. Когда был создан объект класса, мы сначала вызвали set — функцию , для того, чтобы задать текущую дату (тем самым проинициализировать элементы данных), а потом — вызвали get — функцию и увидели соответствующую дату на экране. Если бы мы сначала вызвали get — функцию , то вместо даты мы бы увидели какие-то числа — мусор. Так вот, при создании объектов, можно сразу же проинициализировать элементы данных класса, выполняет эту функцию конструктор. Конструктор — специальная функция, которая выполняет начальную инициализацию элементов данных, причём имя конструктора обязательно должно совпадать с именем класса. Важным отличием конструктора от остальных функций является то, что он не возвращает значений вообще никаких, в том числе и void . В любом классе должен быть конструктор,даже если явным образом конструктор не объявлен (как в предыдущем классе), то компилятор предоставляет конструктор по умолчанию, без параметров. Доработаем класс CppStudio , добавив к нему конструктор.

// classes.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include << "\nwebsite: сайт\ntheme: Classes and Objects in C + +\n"; } void setDate(int date_day, int date_month, int date_year) // установка даты в формате дд.мм.гг { day = date_day; // инициализация день month = date_month; // инициализация месяц year = date_year; // инициализация год } void getDate() // отобразить текущую дату { cout << "date: " << day << "." << month << "." << year << endl; } }; // конец объявления класса CppStudio int main(int argc, char* argv) { CppStudio objCppstudio(11,11,2011); // объявление объекта и инициализация элементов данных objCppstudio.message(); // вызов функции message objCppstudio.getDate(); // отобразить дату system("pause"); return 0; }

// код Code::Blocks

// код Dev-C++

// classes.cpp: определяет точку входа для консольного приложения. #include using namespace std; class CppStudio // имя класса { private: // спецификатор доступа private int day, // день month, // месяц year; // год public: // спецификатор доступа public CppStudio(int date_day, int date_month, int date_year) // конструктор класса { setDate(date_day, date_month, date_year); // вызов функции установки даты } void message() // функция (метод класса) выводящая сообщение на экран { cout << "\nwebsite: сайт\ntheme: Classes and Objects in C + +\n"; } void setDate(int date_day, int date_month, int date_year) // установка даты в формате дд.мм.гг { day = date_day; // инициализация день month = date_month; // инициализация месяц year = date_year; // инициализация год } void getDate() // отобразить текущую дату { cout << "date: " << day << "." << month << "." << year << endl; } }; // конец объявления класса CppStudio int main(int argc, char* argv) { CppStudio objCppstudio(11,11,2011); // объявление объекта и инициализация элементов данных objCppstudio.message(); // вызов функции message objCppstudio.getDate(); // отобразить дату return 0; }

Конструктор объявлен в строках 13 — 16 . Конструктор имеет три параметра, через которые он получает информацию о дате, в теле конструктора вызывается set — функция для установки даты. Можно было реализовать начальную инициализацию элементов данных класса и без set — функции , но так как эта функция была предусмотрена, то правильнее будет использовать именно эту функцию, строка 15 . В строке 35 объявляем объект класса, причём после имени объекта в круглых скобочках передаём три аргумента. Вот так с помощью конструктора выполняется начальная инициализация элементов данных (см. Рисунок 3).

Website: сайт theme: Classes and Objects in C + + date: 11.11.2011

Рисунок 3 — Классы в С++

Объявление класса в отдельном файле

До сих пор объявление класса выполнялось в файле с главной функцией и всё работало. Предположим, необходимо написать какую-то программу, для этого необходимо воспользоваться классом CppStudio — разработанный ранее нами класс. Чтобы воспользоваться этим классом, необходимо подключить файл, в котором он объявлен. Как мы уже говорили, подключение файлов выполняется с помощью препроцессорной директивы #include . Но даже, если мы сможем подключить файл с классом, появится новая проблема — так как в файле с классом уже есть функция main() , то при построении проекта компилятор выдаст ошибку. Суть ошибки: «В проекте найдено несколько main() — функций .» Именно поэтому класс необходимо объявлять в отдельном файле, чтобы его можно было неоднократно использовать. Ранее мы объявляли в отдельном файле функции, таким же образом размещается класс в отдельном файле. Для этого необходимо выполнить 3 шага:

  1. в заголовочном файле объявить пользовательский класс, в нашем случае — CppStudio ;
  2. подключить заголовочный файл к программе, в нашем случае — #include "CppStudio.h" .

В зависимости от среды разработки, способы добавления файлов в проект могут отличаться, но суть задачи от этого не меняется. В MVS2010 заголовочный файл можно добавить, вызвав контекстное меню(клик правой кнопкой мыши) в «обозревателе решений «, выбрав пункт «создать новый элемент «. В появившемся диалоговом окне выбираем нужный нам тип файлаэто *.h и заполняем поле «Имя файла «. Как и прежде, имя выбираем осмысленное, как правило такое же как и имя класса. Теперь к нашему проекту добавлен новый заголовочный файл — CppStudio.h .

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

// заголовочный файл CppStudio.h #include using namespace std; // объявление класса class CppStudio // имя класса { private: // спецификатор доступа private int day, // день month, // месяц year; // год public: // спецификатор доступа public CppStudio(int date_day, int date_month, int date_year) // конструктор класса { setDate(date_day, date_month, date_year); // вызов функции установки даты } void message() // функция (метод класса) выводящая сообщение на экран { cout << "nwebsite: сайтntheme: Classes and Objects in C + +n"; } void setDate(int date_day, int date_month, int date_year) // установка даты в формате дд.мм.гг { day = date_day; // инициализация день month = date_month; // инициализация месяц year = date_year; // инициализация год } void getDate() // отобразить текущую дату { cout << "date: " << day << "." << month << "." << year << endl; } }; // конец объявления класса CppStudio

Чтобы главная функция увидела созданный нами класс и смогла его использовать, необходимо включить определение класса в исполняемом файле, с функцией main() . Делается это так:

// код Code::Blocks

// код Dev-C++

// classes.cpp: определяет точку входа для консольного приложения. // подключаем класс CppStudio #include "CppStudio.h" int main(int argc, char*argv) { CppStudio objCppstudio(11,11,2011); // объявление объекта и инициализвция элементов данных objCppstudio.message(); // вызов функции message objCppstudio.getDate(); // отобразить дату system("pause"); return 0; }

В строке 5 подключеноопределение класса CppStudio , только после этого можно создавать объекты класса, использовать его методы и т. д. Результат работы программы точно такой же как и раньше, изменилась лишь структура проекта.

Отделение интерфейса от реализации

Интерфейс класса — конструкция, определяющая методы и свойства, предоставляемые классом. Реализация класса — это способ осуществления работоспособности класса. До этого мы не отделяли интерфейс класса от его реализации, то есть реализация методов осуществлялась внутри класса. Отделение интерфейса от реализации класса выполняется для того, чтобы скрыть способ осуществления работоспособности класса. Отделение интерфейса от реализации выполняется за 5 шагов:

  1. добавить в проект заголовочный файл *.h ;
  2. определить интерфейс класса в заголовочном файле
  3. добавить в проект исполняемый файл *.cpp ;
  4. в исполняемом файле выполнить реализацию класса;
  5. подключить заголовочный файл к программе.

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

// заголовочный файл класса СppStudio.h // интерфейс класса // объявление класса class CppStudio // имя класса { private: // спецификатор доступа private int day, // день month, // месяц year; // год public: // спецификатор доступа public CppStudio(int, int, int); // конструктор класса void message(); // функция (метод класса) выводящая сообщение на экран void setDate(int, int, int); // установка даты в формате дд.мм.гг void getDate(); // отобразить текущую дату }; // конец объявления класса CppStudio

В интерфейсе класса остались объявленные переменные и прототипы методов класса. Теперь рассмотрим содержимое файла реализации методов класса.

// файл реализации класса CppStudio.cpp #include using namespace std; // подключаем интерфейс класса к файлу его реализации #include "CppStudio.h" CppStudio::CppStudio(int date_day, int date_month, int date_year) // конструктор класса { setDate(date_day, date_month, date_year); // вызов функции установки даты } void CppStudio::message() // функция (метод класса) выводящая сообщение на экран { cout << "nwebsite: сайтntheme: Classes and Objects in C + +n"; } void CppStudio::setDate(int date_day, int date_month, int date_year) // установка даты в формате дд.мм.гг { day = date_day; // инициализация день month = date_month; // инициализация месяц year = date_year; // инициализация год } void CppStudio::getDate() // отобразить текущую дату { cout << "date: " << day << "." << month << "." << year << endl; }

Чтобы связать интерфейс класса и его реализацию, необходимо в файле реализации подключить заголовочный файл с определением класса, строка 6 (выделенная строка). После этого можно объявлять методы класса. Методы класса объявляются точно так же как и функции, только перед именем метода необходимо написать имя класса и поставить унарную операцию разрешения области действия «:: «.

// синтаксис объявления методов класса вне тела класса /*возвращаемый тип данных*/ /*имя класса*/::/*имя метода*/(/*параметры метода*/) { // операторы }

Так как методы класса объявляются вне тела класса, то необходимо связать реализацию метода с классом, а для этого воспользуемся бинарной операцией разрешения области действия. Бинарная операция разрешения области действия привязывает метод, объявленный извне, к классу, имя которого совпадает с именем в объявлении метода. Вот именно поэтому в объявлении метода класса необходимо добавлять имя класса и операцию разрешения области действия.

Итак, интерфейс класса определён, методы класса объявлены, осталось подключить заголовочный файл в исполняемом файле с main() функцией и программа готова.

// classes.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" // подключаем класс CppStudio #include "CppStudio.h" int main(int argc, char*argv) { CppStudio objCppstudio(11,11,2011); // объявление объекта и инициализвция элементов данных objCppstudio.message(); // вызов функции message objCppstudio.getDate(); // отобразить дату system("pause"); return 0; }

// код Code::Blocks

// код Dev-C++

// classes.cpp: определяет точку входа для консольного приложения. // подключаем класс CppStudio #include "CppStudio.h" int main(int argc, char*argv) { CppStudio objCppstudio(11,11,2011); // объявление объекта и инициализвция элементов данных objCppstudio.message(); // вызов функции message objCppstudio.getDate(); // отобразить дату return 0; }

В строке 5 подключаем заголовочный файл класса, после чего можно создавать объекты этого класса.

Несколько советов о том, как следует формировать интерфейс будущего класса и что для этого надо или не надо делать.