Основы С++
Основу ООП в С++ составляет класс. Класс – это расширение концепции структуры в си. Поэтому начнём со сравнения класса и структуры. Пусть у нас имеется структура «Ящик» . Ящик имеет высоту, глубину и ширину.
struct Box { unsigned height; unsigned width; unsigned height; };
Можно определить новый тип данных Box, используя служебное слово typedef
typedef struct { unsigned height; unsigned width; unsigned height; } Box;
Каждое поле структуры может быть напрямую изменено.
Box b. b.height = 10;
Для того, чтобы найти объём ящика, необходимо написать функцию, которая получает в качестве аргумента структуру.
unsigned getVolume(Box *b) { return b->height * b->width * b->depth; }
Какие здесь недостатки? Во-первых, есть прямой доступ до полей структуры. Поля могут быть изменены любым способом, в том числе, могут быть заданы неправильные значения. Во-вторых, для работы со структурой приходится писать функции, которые принимают один и тот же аргумент (структуры, с которой необходимо работать). Функций становится много, и каждая из них должна иметь уникальное имя. Класс позволяет избавиться от этих проблем, и кроме того, приносит много других полезных особенностей, с которыми мы будем разбираться весь семестр.
Вот класс ящик, который копирует описанную выше структуру.
class Box { public: unsigned height; unsigned depth; unsigned width; unsigned getVolume() { return depth*height*width; } };
Основные отличия
- 1. Мы используем служебное слово class.
- 2. Появился модификатор доступа public, который делает поля класса открытыми, то есть, они могут быть изменены также, как и поля структуры.
- 3. Класс содержит метод getVolume, который возвращает объём. При этом метод принадлежит экземпляру класса, и может обращаться к своим полям.
- 4. Мы не используем typedef, так как класс становится сразу новым типом данных.
Класс является планом, проектом, шаблоном (в широком смысле), по которому создаётся экземпляр класса. Экземпляр класса называют объектом.
#include <iostream> class Box { public: unsigned height; unsigned depth; unsigned width; unsigned getVolume() { return depth*height*width; } }; void main() { Box b, c; b.depth = 10; b.height = 20; b.width = 30; c.depth = 100; c.height = 200; c.width = 300; std::cout << "B volume = " << b.getVolume() << std::endl; std::cout << "C volume = " << c.getVolume() << std::endl; system("PAUSE"); }
Теперь разберёмся с терминологией. public – это модификатор доступа. Он определяет, кто может обращаться к полям объекта. В нашем случае модификатор говорит о том, что к полям может обращаться кто угодно.
height, depth и width – это поля класса, которые называют данными-членами класса, или его свойствами. Поля каждого экземпляра хранят собственные значения и определяют состояние класса (далее буду всегда именовать данные-члены класса свойствами класса).
getVolume – это функция-член класса, или метод. Метод может обращаться к полям класса напрямую, независимо от модификатора доступа (далее, все функции-члены класса я буду именовать методами).
b и c – это объекты, которые являются экземплярами класса Box.
Начнём теперь усложнять программу. Во-первых, нужно ограничить доступ до полей класса, чтобы никто не смог просто так взять и изменять их.
class Box { protected: unsigned height; unsigned depth; unsigned width; public: unsigned getVolume() { return depth*height*width; } };
Теперь мы не сможем написать
Box b, c; b.depth = 10; b.height = 20; b.width = 30; c.depth = 100; c.height = 200; c.width = 300;
так как поля стали недоступны извне класса.
Для того, чтобы получить возможность изменять значения полей, напишем ряд методов, которые возвращают значения полей и устанавливают новые значения для полей.
class Box { protected: unsigned height; unsigned depth; unsigned width; public: unsigned getVolume() { return depth*height*width; } void setDepth(unsigned depth) { this->depth = depth; } void setHeight(unsigned height) { this->height = height; } void setWidth(unsigned w) { width = w; } unsigned getDepth() { return depth; } unsigned getHeight() { return this->height; } unsigned getWidth() { return this->width; } };
Здесь служебное слово this означает обращение к самому себе. То есть
void setDepth(unsigned depth) { this->depth = depth; }
значит установить собственному значению depth переданное значение аргумента depth.
Работа с экземплярами класса выглядит сейчас так
void main() { Box b, c; b.setDepth(10); b.setHeight(20); b.setWidth(30); c.setDepth(100); c.setHeight(200); c.setWidth(300); std::cout << "B volume = " << b.getVolume() << std::endl; std::cout << "C volume = " << c.getVolume() << std::endl; system("PAUSE"); }
Теперь мы обращаемся к методам класса для доступа к защищённым полям. Ещё раз обращаю внимание: методы класса могут обращаться к собственным полям, независимо от модификатора доступа. А функции извне класса не могут обратиться к полям класса, определённым с модификатором protected (или private), но могут, если поля определены как public.
Такое сокрытие состояния объекта называется инкапсуляцией.
Методы, между тем, остаётся публичными, чтобы ими можно было воспользоваться.
Правильное описание класса
Класс в си принято (нужно) описывать в отдельных файлах. Обычно файлы имеют то же имя, что и класс. Для нашего класса создадим два файла: Box.h, с
объявление класса, и Box.cpp с определением.
Box.h
#pragma once class Box { protected: unsigned height; unsigned depth; unsigned width; public: unsigned getVolume(); void setDepth(unsigned depth); void setHeight(unsigned height); void setWidth(unsigned w); unsigned getDepth(); unsigned getHeight(); unsigned getWidth(); };
Box.cpp
#include "Box.h" unsigned Box::getVolume() { return depth*height*width; } void Box::setDepth(unsigned depth) { this->depth = depth; } void Box::setHeight(unsigned height) { this->height = height; } void Box::setWidth(unsigned w) { width = w; } unsigned Box::getDepth() { return depth; } unsigned Box::getHeight() { return this->height; } unsigned Box::getWidth() { return this->width; }
main.cpp
#include <iostream> #include "Box.h" void main() { Box b, c; b.setDepth(10); b.setHeight(20); b.setWidth(30); c.setDepth(100); c.setHeight(200); c.setWidth(300); std::cout << "B volume = " << b.getVolume() << std::endl; std::cout << "C volume = " << c.getVolume() << std::endl; system("PAUSE"); }
P.S. о std::cout, std::cin и system
Мы используем конструкцию std::cout << для вывода значений в консоль. Почему это пишется так, а не иначе, узнаем потом, после изучения темы переопределение операторов и пространств имён. Сейчас просто запомните, что для использования cout нужно подключить библиотеку iostream (input output stream). Для ввода значений из консоли можно использовать конструкцию std::cin >>.
#include <iostream> // для cin и cout #include <windows.h> // для system void main() { int x; float y; //устанавливаем локаль для работы с русским языком setlocale(LC_ALL, "ru"); //вывод строки std::cout << "Введите x: "; //ввод числа std::cin >> x; std::cout << "Введите y: "; std::cin >> y; //для вывода нескольких переменных или строк их можно разделять //с помощью << std::cout << "x = " << x << std::endl; //std::endl - это символ перевода строки std::cout << "y = " << y << std::endl; system("pause"); }
system - это функция, которая вызывает системные команды. В данном случае pause (или Pause, или PaUSe, вызов регистронезависим) останавливает выполнение программы в консоли до тех пор, пока не будет нажата клавиша.