Классы и объекты в С++ являются основными концепциями объектно-ориентированного программирования — ООП. Объектно-ориентированное программирование — расширение структурного программирования, в котором основными концепциями являются понятия классов и объектов. Основное отличие языка программирования С++ от С состоит в том, что в С нет классов, а следовательно язык С не поддерживает ООП, в отличие от С++.
Чтобы понять, для чего же в действительности нужны классы, проведём аналогию с каким-нибудь объектом из повседневной жизни, например, с велосипедом. Велосипед — это объект, который был построен согласно чертежам. Так вот, эти самые чертежи играют роль классов в ООП. Таким образом классы — это некоторые описания, схемы, чертежи по которым создаются объекты. Теперь ясно, что для создания объекта в ООП необходимо сначала составить чертежи, то есть классы. Классы имеют свои функции, которые называются методами класса. Передвижение велосипеда осуществляется за счёт вращения педалей, если рассматривать велосипед с точки зрения ООП, то механизм вращения педалей — это метод класса. Каждый велосипед имеет свой цвет, вес, различные составляющие — всё это свойства. Причём у каждого созданного объекта свойства могут различаться. Имея один класс, можно создать неограниченно количество объектов (велосипедов), каждый из которых будет обладать одинаковым набором методов, при этом можно не задумываться о внутренней реализации механизма вращения педалей, колёс, срабатывания системы торможения, так как всё это уже будет определено в классе. Разобравшись с назначением класса, дадим ему грамотное определение.
Классы в С++ — это абстракция описывающая методы, свойства, ещё не существующих объектов. Объекты — конкретное представление абстракции, имеющее свои свойства и методы. Созданные объекты на основе одного класса называются экземплярами этого класса. Эти объекты могут иметь различное поведение, свойства, но все равно будут являться объектами одного класса. В ООП существует три основных принципа построения классов:
- Инкапсуляция — это свойство, позволяющее объединить в классе и данные, и методы, работающие с ними и скрыть детали реализации от пользователя.
- Наследование — это свойство, позволяющее создать новый класс-потомок на основе уже существующего, при этом все характеристики класса родителя присваиваются классу-потомку.
- Полиморфизм — свойство классов, позволяющее использовать объекты классов с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.
Каждое свойство построения классов мы рассмотрим подробно по мере необходимости, а пока просто запомните эти три. А теперь вернёмся к классам, для начала рассмотрим структуру объявления классов.
// объявление классов в С++ class /*имя класса*/ { private: /* список свойств и методов для использования внутри класса */ public: /* список методов доступных другим функциям и объектам программы */ protected: /*список средств, доступных при наследовании*/ };
Объявление класса начинается с зарезервированного ключевого слова class, после которого пишется имя класса. В фигурных скобочках, строки 3 — 10 объявляется тело класса, причём после закрывающейся скобочки обязательно нужно ставить точку с запятой, строка 10. В теле класса объявляются три метки спецификации доступа, строки 4, 6, 8, после каждой метки нужно обязательно ставить двоеточие. В строке 4 объявлена метка спецификатора доступа private
. Все методы и свойства класса, объявленные после спецификатор доступа private
будут доступны только внутри класса. В строке 6 объявлен спецификатор доступа public
, все методы и свойства класса, объявленные после спецификатора доступа public
будут доступны другим функциям и объектам в программе. Пока на этом остановимся, спецификатор доступа protected
разбирать сейчас не будем, просто запомните, что он есть. При объявлении класса, не обязательно объявлять три спецификатора доступа, и не обязательно их объявлять в таком порядке. Но лучше сразу определиться с порядком объявления спецификаторов доступа, и стараться его придерживаться. Разработаем программу, в которой объявим простейший класс, в котором будет объявлена одна функция, печатающая сообщение.
// classes.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include <iostream> using namespace std; // начало объявления класса class CppStudio // имя класса { public: // спецификатор доступа void message() // функция (метод класса) выводящая сообщение на экран { cout << "website: cppstudio.com\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: cppstudio.com theme: Classes and Objects in C + +
Рисунок 1 — Классы в С++
set — функции и get — функции классов
Каждый объект имеет какие-то свои свойства или атрибуты, которые характеризуют его на протяжении всей жизни. Атрибуты объекта хранятся в переменных, объявленных внутри класса, которому принадлежит данный объект. Причём, объявление переменных должно выполняться со спецификатором доступа private
. Такие переменные называются элементами данных. Так как элементы данных объявлены в private
, то и доступ к ним могут получить только методы класса, внешний доступ к элементам данных запрещён. Поэтому принято объявлять в классах специальные методы — так называемые set
и get
функции, с помощью которых можно манипулировать элементами данных. set-функции инициализируют элементы данных, get-функции позволяют просмотреть значения элементов данных. Доработаем класс CppStudio
так, чтобы в нём можно было хранить дату в формате дд.мм.гг. Для изменения и просмотра даты реализуем соответственно set
и get
функции.
// classes.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include <iostream> using namespace std; class CppStudio // имя класса { private: // спецификатор доступа private int day, // день month, // месяц year; // год public: // спецификатор доступа public void message() // функция (метод класса) выводящая сообщение на экран { cout << "\nwebsite: cppstudio.comntheme: 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: cppstudio.comntheme: Classes and Objects in C + + Date: 10.11.2011
Рисунок 2 — Классы в С++
Конструкторы
В предыдущей программе, у класса CppStudio
были объявлены элементы данных, которые могут хранить информацию о дате. Когда был создан объект класса, мы сначала вызвали set — функцию, для того, чтобы задать текущую дату (тем самым проинициализировать элементы данных), а потом — вызвали get — функцию и увидели соответствующую дату на экране. Если бы мы сначала вызвали get — функцию, то вместо даты мы бы увидели какие-то числа — мусор. Так вот, при создании объектов, можно сразу же проинициализировать элементы данных класса, выполняет эту функцию конструктор. Конструктор — специальная функция, которая выполняет начальную инициализацию элементов данных, причём имя конструктора обязательно должно совпадать с именем класса. Важным отличием конструктора от остальных функций является то, что он не возвращает значений вообще никаких, в том числе и void
. В любом классе должен быть конструктор,даже если явным образом конструктор не объявлен (как в предыдущем классе), то компилятор предоставляет конструктор по умолчанию, без параметров. Доработаем класс CppStudio
, добавив к нему конструктор.
// classes.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include <iostream> 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: cppstudio.com\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 <iostream> 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: cppstudio.com\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: cppstudio.com theme: Classes and Objects in C + + date: 11.11.2011
Рисунок 3 — Классы в С++
Объявление класса в отдельном файле
До сих пор объявление класса выполнялось в файле с главной функцией и всё работало. Предположим, необходимо написать какую-то программу, для этого необходимо воспользоваться классом CppStudio
— разработанный ранее нами класс. Чтобы воспользоваться этим классом, необходимо подключить файл, в котором он объявлен. Как мы уже говорили, подключение файлов выполняется с помощью препроцессорной директивы #include
. Но даже, если мы сможем подключить файл с классом, появится новая проблема — так как в файле с классом уже есть функция main()
, то при построении проекта компилятор выдаст ошибку. Суть ошибки: «В проекте найдено несколько main()
— функций.» Именно поэтому класс необходимо объявлять в отдельном файле, чтобы его можно было неоднократно использовать. Ранее мы объявляли в отдельном файле функции, таким же образом размещается класс в отдельном файле. Для этого необходимо выполнить 3 шага:
- добавить в проект заголовочный файл
*.h
; - в заголовочном файле объявить пользовательский класс, в нашем случае —
CppStudio
; - подключить заголовочный файл к программе, в нашем случае —
#include "CppStudio.h"
.
?В зависимости от среды разработки, способы добавления файлов в проект могут отличаться, но суть задачи от этого не меняется. В MVS2010 заголовочный файл можно добавить, вызвав контекстное меню(клик правой кнопкой мыши) в «обозревателе решений«, выбрав пункт «создать новый элемент«. В появившемся диалоговом окне выбираем нужный нам тип файла — это *.h
и заполняем поле «Имя файла«. Как и прежде, имя выбираем осмысленное, как правило такое же как и имя класса. Теперь к нашему проекту добавлен новый заголовочный файл — CppStudio.h
.
В только что созданном заголовочном файле объявляем класс и, если необходимо, подключаем дополнительные заголовочные. Вот, что должно было получиться:
// заголовочный файл CppStudio.h #include <iostream> 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: cppstudio.comntheme: 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()
. Делается это так:
// 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(); // отобразить дату system("pause"); return 0; }
В строке 5 подключено определение класса CppStudio
, только после этого можно создавать объекты класса, использовать его методы и т. д. Результат работы программы точно такой же как и раньше, изменилась лишь структура проекта.
Отделение интерфейса от реализации
Интерфейс класса — конструкция, определяющая методы и свойства, предоставляемые классом. Реализация класса — это способ осуществления работоспособности класса. До этого мы не отделяли интерфейс класса от его реализации, то есть реализация методов осуществлялась внутри класса. Отделение интерфейса от реализации класса выполняется для того, чтобы скрыть способ осуществления работоспособности класса. Отделение интерфейса от реализации выполняется за 5 шагов:
- добавить в проект заголовочный файл
*.h
; - определить интерфейс класса в заголовочном файле
- добавить в проект исполняемый файл
*.cpp
; - в исполняемом файле выполнить реализацию класса;
- подключить заголовочный файл к программе.
Как добавить заголовочный файл в проект мы уже знаем, точно также добавляется и исполняемый файл, имена этим файлам даются, как правило, одинаковые. Интерфейс класса должен выглядеть так:
// заголовочный файл класса С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 <iostream> 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: cppstudio.comntheme: 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 подключаем заголовочный файл класса, после чего можно создавать объекты этого класса.
Несколько советов о том, как следует формировать интерфейс будущего класса и что для этого надо или не надо делать.
Комментарии
lvl 54 Boris
В чем разница между:
MyClass m;
и
MyClass *m = new MyClass();
Я понимаю типа выделение памяти и все дела, но не понимаю накой черт это надо если работает и без new, объясните пжлст )
Evgenii
Здравствуйте. Не могу понять, чем отличается унарная операция разрешения области действия от бинарной?
Gosha
Совет. Создавайте классы, используя средства своей IDE:
В MVS в меню Проект->Мастер классов или Проект->Добавить класс
В Code::Blocks в меню File->New->Class
В Dev-C++ слева выбрать закладку Классы и правой кнопкой в контекстном меню выбрать Новый класс
В QTCreator в меню Новый файл или проект, в разделе Файлы и классы выбрать C++ и соответственно Класс C++
blablabla
Зачем нужны строки 21-25, почему бы не сделать инициализацию напрямую?