1: // Объявление класса Cat
2: // Переменные-члены объявляются закрытыми, а открытые методы доступа
3: // обеспечивают инициализацию переменных-членов и возвращение их значений
4:
5: class Cat
6: {
7: public:
8: // открытые методы доступа
9: unsigned int GetAge();
10: void SetAge(unsigned int Age);
11:
12: unsigned int GetWeight();
13: void SetWeight(unsigned int Weight);
14:
15: // открытые функции-члены
16: void Meow();
17:
18: // закрытые переменные-члены
19: private:
20: unsigned int itsAge;
21: unsigned int itsWeight;
22:
23: };
Анализ: Этот класс имеет пять открытых методов. В строках 9 и 10 содержатся :/:<<**v>>>>* объявления методов обеспечения доступа к переменной-члену itsAge. А в строках 12 и 13 объявляются методы доступа к переменной-члену itsWeight. Эти функции-члены инициализируют переменные и возвращают их значения.
В строке 16 объявляется открытая функция-член Meow(). Функция Meow() не является методом доступа. Она не получает и не устанавливает значение переменной-члена, а выполняет другой вид сервиса для класса, выводя слово Meow на экран.
Сами переменные-члены объявляются в строках 20 и 21.
Чтобы установить возраст кота Frisky, нужно передать соответствующее значение методу SetAge():
Cat Frisky;
Frisky.SetAge(5); // устанавливаем возраст Frisky с помощью открытого метода-доступа
Ограничение доступа к данным - это не способ защиты данных, а лишь средство облегчения программирования
Объявление методов или данных закрытыми позволяет компилятору заблаговременно находить ошибки программирования. Начинающие программисты часто ошибочно полагают, что объявляя данные закрытыми, тем самым скрывают некоторую секретную информацию от пользователей, не имеющих соответствующих прав доступа. В действительности это не так. По этому поводу Страустрап (Stroustrup), изобретатель языка C++, сказал: "Механизмы управления доступом в C++ обеспечивают защиту от несчастного случая, но не от мошенника" (ARM, 1990).
Рекомендуется:Объявляйте закрытыми переменные- члены класса (с помощью ключевого слова private).
Объявляйте открытыми методы доступа к закрытым данным-членам класса. Используйте для обработки данных-членов закрытые функции-члены класса.
Не рекомендуется:Не пытайтесь использовать закрытые переменные-члены вне класса.
Ключевое слово class
Ключевое слово class имеет следующий синтаксис:
class имя_класса
{
// здесь находятся ключевые слова управления доступом
// здесь объявляються переменные и методы класса
};
Ключевое слово class используется для объявления новых типов. Класс - это коллекция данных-членов класса, которые представляют собой переменные различных типов, включая другие классы. Класс также содержит функции класса, или методы, которые используются для выполнения действий над данными класса, а также для выполнения других видов сервиса внутри класса.
Определение объектов нового типа во многом подобно определению любых переменных. Сначала указывается тип (класс), а затем имя переменной (объект). Для обращения к членам класса Данным и функциям) используется оператор точки (.). Для объявления открытых или закрытых разделов класса используются ключевые слова управления доступом public или private. По умолчанию действует закрытый режим доступа. Каждое ключевое слово изменяет режим управления доступом с момента использования этого ключевого слова и до конца объявления класса или до тех пор, пока не встретится следующее ключевое слово управления доступом. Все объявления классов оканчиваются закрывающей фигурной скобкой и точкой с запятой.
Пример 1:
class Cat
{
public:
unsigned int Age;
unsigned int Weight;
void Meow();
}
Cat Frisky;
Frisky.Age = 8;
Frisky.Weight = 18;
Frisky.Meow();
Пример 2:
class Car
{
public:
void Start();
void Accelerate();
void Brake();
void SetYear(int year);
int GetYear();
private:
int Year;
Char Model[255];
};
Car OldFaithful;
int bought;
OldFaithful.SetYear(84);
bought = OldFaithful.GetYear();
OldFaithful.Start();
Определение методов класса
Как упоминалось выше, методы доступа обеспечивают интерфейс для работы с закрытыми переменными-членами класса. Для методов доступа, как и для всех других объявленных методов класса, следует определять выполнение. Таким образом, методы объявляются и определяются в классе.
Определение функции-члена начинается с имени класса, за которым следуют два двоеточия, имя функции и ее параметры. В листинге 6.3 показано объявление простого класса Cat, в котором присутствуют определения ранее объявленных методов доступа к данным и одной обычной функции-члена.
Листинг 6.3. определение методов простого класса
1: // Пример определения методов в
2: // объявлении класса
3:
4: #include <iostream.h> // для объекта cout
5:
6: class Cat // начало объявления класса
7: {
8: public: // начало раздела public
9: int GetAgeO; // метод доступа
10: void SetAge (int age); // метод доступа
11: void Meow(); // обычный метод
12: private: // начало раздела
13: int itsAge; // переменная-член
14: };
15:
16: // GetAge, открытая функция доступа,
17:// возвращает значение переменной-члена itsAge
18: int Cat::GetAge()
19: {
20: return itsAge;
21: }
22:
23: // Определение открытой функции доступа SetAge
24: // Функция SetAge
25: // инициирует переменную-член itsAge
26: void Cat::SetAge(int age)
27: {
28: // устанавливаем переменную-член itsAge равной
29: // значению, переданному с помощью параметра age
30: itsAge = age;
31: }
32:
33: // Определение метода Meow
34: // возвращает void
35: // параметров нет
36: // используется для вывода на экран текста "Meow"
37: void Cat::Meow()
38: {
39: cout << "Meow.\n";
40: }
41:
42: // Создаем виртуальную кошку, устанавливаем ее возраст, разрешаем
43: // ей мяукнуть, сообщаем ее возраст, затем снова "мяукаем".
44: int main()
45: {
46: Cat Frisky;
47: Frisky.SetAge(5);
48: Frisky.Meow();
49: cout << "Frisky is а cat who is ";
50: cout << Frisky.QetAge() << " years old.\n";
51: Frisky.Meow();
52: return 0;
53: }
Результат:
Meow.
Frisky is а cat who is 5 years old.
Meow.
Анализ: В строках 6-14 содержится определение класса Cat. Строку 8 занимает ключевое слово public, которое сообщает компилятору, что за ним следует набор открытых членов класса. В строке 9 содержится объявление открытого метода GetAge(), который предоставляет доступ к закрытой переменной-члену itsAge, объявляемой в строке 13. В строке 10 объявляется открытая функция доступа SetAge(), которая принимает в качестве аргумента целочисленное значение и присваивает переменной itsAge значение этого аргумента.
В строке 11 объявляется метод Meow(). Этот метод не является функцией доступа к данным-членам класса, а используется для вывода на экран слова Meow.
В строке 12 начинается закрытый раздел, который включает только одно объявление закрытой переменной-члена itsAge (строка 13). Объявление класса завершается закрывающей фигурной скобкой и точкой с запятой в строке 14.
Строки 18-21 содержат определение функции-члена GetAge(). Этот метод не принимает никаких параметров и возвращает целое значение. Обратите внимание на то, что
при определении методов класса используется имя класса, за которым следуют два двоеточия и имя функции (строка 18). Благодаря этому-синтаксису компилятор узнает, что определяемая здесь функция GetAge() - это функция, объявленная в классе Cat. За исключением строки заголовка, GetAge() создается точно так же, как и другие функции.
Определение функции GetAge() занимает только одну строку, в которой указывается, что эта функция возвращает значение переменной-члена itsAge. Обратите внимание, что функция main() не может получить доступ к этой переменной, поскольку она объявлена в закрытом разделе класса Cat. При этом из функции main() можно обратиться к открытому методу GetAge(). А поскольку метод GetAge() является функцией-членом класса Cat, то он имеет все права доступа к переменной-члену itsAge. В результате функция GetAge() возвращает значение переменной itsAge в функцию main().
В строке 26 начинается определение функции-члена SetAge(). Она принимает целочисленный параметр и присваивает переменной itsAge значение этого параметра (строка 30). Являясь членом класса Cat, функция SetAge() имеет прямой доступ к переменной-члену itsAge.
В строке 37 начинается определение метода Meow() класса Cat. Этот метод занимает всего одну строку, в которой выводится на экран слово Meow, а затем выполняется переход на новую строку. Помните, что для перехода на новую строку используется символ \n.
В строке 44 начинается тело функции main(); она не принимает никаких аргументов. В строке 46 в функции main() объявляется объект класса Cat с именем Frisky. В строке 47 переменной-члену itsAge присваивается значение 5 с помощью метода доступа SetAge(). Обратите внимание, что в вызове этого метода указывается имя объекта (Frisky), за которым следует оператор прямого доступа (.), и имя самого метода (SetAge()). Таким способом можно вызывать любые другие методы класса.
В строке 48 вызывается функция-член Meow(), а в строке 49 на экран выводится значение переменной-члена с использованием функции доступа GetAge(). В строке 51 функция Meow() вызывается снова.
Конструкторы и деструкторы
Существует два способа определения целочисленной переменной. Во-первых, можно определить переменную, а затем (несколько ниже в программе) присвоить ей некоторое значение, например:
int Weight;
// определяем переменную
// здесь следуют другие выражения Weight = 7; // присваиваем значение переменной
Можно также определить переменную и немедленно ее инициализировать, например:
int Weight = 7; // определяем и инициализируем значением 7.
Операция инициализации сочетает в себе определение пербмбнной с присвоением начального значения. Причем ничто не может помешать вам впоследствии изменить это значение. Кроме того, инициализация, проведенная одновременно с определением, гарантирует, что переменная не будет содержать мусор, оставшийся в выделенных переменной ячейках памяти.
Как же инициализировать переменные-члены класса? Для этого в классе используется специальная функция-член, называемая конструктором. При необходимости конструктор может принимать параметры, но не может возвращать значения даже типа void. Конструктор - это метод класса, имя которого совпадает с именем самого класса.
Объявив конструктор, вам также стоит объявить и деструктор. Если конструкторы служат для создания и инициализации объектов класса, то деструкторы удаляют из памяти отработавшие объекты и освобождают выделенную для них память. Деструктору всегда присваивается имя класса с символом тильды (~) вначале. Деструкторы не принимают никаких аргументов и не возвращают никаких значений. Объявление деструктора класса Cat будет выглядеть следующим образом:
~Cat();
Конструкторы и деструкторы, заданные по умолчанию
Если вы не объявите конструктор или деструктор, то компилятор сделает это за вас. Стандартные конструктор и деструктор не принимают аргументов и не выполняют никаких действий.
Вопросы и ответы: Конструктор называется стандартным из-за отсутствия аргументов или из-за того, что создается компилятором в том случае, если в классе не объявляется никакой другой конструктор?
Стандартный конструктор, или конструктор по умолчанию, характеризуется тем, что не принимает никаких аргументов, причем неважно, создан ли этот конструктор автоматически компилятором или самим программистом. Стандартный конструктор всегда используется по умолчанию.
Однако что касается деструкторов, то тут есть свои отличия. Стандартный деструктор предоставляется компилятором. Поскольку все деструкторы не имеют параметров, то главной отличительной чертой стандартного деструктора является то, что он не выполняет никаких действий, т.е. имеет пустое тело функции.
Использование конструктора, заданного по умолчанию
Какая же польза от конструктора, который ничего не выполняет? Зачастую это нужно только для протокола. Все объекты должны быть локализованы в программе, поэтому их создание и удаление сопровождается вызовом соответствующей функции, которая при этом может ничего и не делать. Так, для объявления объекта без передачи параметров, например:
Cat Rags; // Rags не получает никаких параметров
необходимо иметь следующий конструктор:
Cat();
Конструктор вызывается при определении объекта класса. Если для создания объекта класса Cat следует передать два параметра, то конструктор класса Cat определяется следующим образом:
Cat Frisky (5,7);
Если конструктор принимает один параметр, определение объекта будет иметь следующий вид:
Cat Frisky (3);
В случае, когда конструктор вообще не принимает параметров (т.е. является стандартным), отпадает необходимость использования круглых скобок:
Cat Frisky;
Этот случай является исключением из правила, гласящего, что все функции требуют наличия круглых скобок, даже если они вовсе не принимают параметров. Вот почему можно спокойно записать такое определение:
Cat Frisky;
Эта запись интерпретируется как обращение к стандартному конструктору. В ней отсутствует передача параметров и, как следствие, круглые скобки.
Обратите внимание, что вы не обязаны постоянно использовать стандартный конструктор, предоставляемый компилятором. Всегда можно написать собственный стандартный конструктор, т.е. конструктор без параметров. Вы вольны наделить свой стандартный конструктор телом функции, в котором будет выполняться инициализация класса.
Чтобы придать законченность своему труду, при объявлении конструктора не забудьте объявить и деструктор, даже если вашему деструктору нечего делать. И хотя справедливо то, что и стандартный конструктор будет корректно работать, отнюдь не повредит объявить собственный деструктор. Это сделает вашу программу более ясной.
В листинге 6.4 в знакомый уже вам класс Cat добавлены конструктор и деструктор. Конструктор используется для инициализации объекта Cat и установки его возраста равным предоставляемому вами значению. Обратите внимание на то, в каком месте программы вызывается деструктор.
Листинг 6.4. Использование конструкторов и деструкторов.
1: // Пример объявления конструктора и
2: // деструктора в классе Cat
3:
4: #include <iostream.h> // для объекта cout
5:
6: class Cat // начало объявления класса
7: {
8: public: // начало открытого раздела
9: Cat(int initialAge); // конструктор
10: ~Cat(); //деструктор
11: int GetAge(); // метод доступа
12: void SetAge(int age); // метод доступа
13: void Meow();
14: private: // начало закрытого раздела
15: int itsAge; // переменная-член
16: };
17:
18: // конструктор класса Cat
19: Cat::Cat(int initialAge)
20: {
21: itsAge = initialAge;
22: }
23:
24: Cat::~Cat() // деструктор, не выполняющий действий
25: {
26: }
27:
28: // GetAge, открытая функция обеспечения доступа,
29: // возвращает значение переменной-члена itsAge
30: int Cat::GetAge()
31: {
32: return itsAge;
33: }
34:
35: // Определение SetAge, открытой
36: // функции обеспечения доступа
37:
38: voidCat::SetAge(int age)
39: {
40: // устанавливаем переменную-член itsAge равной
41: // значению, переданному параметром age
42: itsAge = age;
43: }
44:
45: // Определение метода Meow
46: // возвращает void
47: // параметров нет
48: // используется для вывода на экран текста "Meow"
49: void Cat::Meow()
50: {
51: cout << "Meow.\n";
52: }
53:
54: // Создаем виртуальную кошку, устанавливаем ее возраст, разрешаем
55: // ей мяукнуть, сообщаем ее возраст, затем снова "мяукаем" и изменяем возраст кошки.
56: int main()
57: {
58: Cat Frisky(5);
59: Frisky.Meow();
60: cout << "Frisky is а cat who is ";
61: cout << Frisky.QetAge() << " years old.\n";
62: Frisky.Meow();
63: Frisky.SetAge(7);
64; cout << "Now Frisky is ";
65: cout << Frisky. GeMje() << " years old.\n";
66: return 0;
67: }
Результат:
Meow.
Frisky is a cat who is 5 years old.
Meow.
Now Frisky is 7 years old.
Анализ: Листинг 6.4 подобен листингу б.З за исключением того, что в строке 9 добавляется конструктор, который принимает в качестве параметра целочисленное значение. В строке 10 объявляется деструктор, который не принимает никаких параметров. Помните, что деструкторы никогда не принимают параметров; кроме того, ни конструкторы, ни деструкторы не возвращают никаких значений - даже значения типа void.
В строках 19-22 определяется выполнение конструктора, аналогичное выполнению функции доступа SetAge(), которая также не возвращает никакого значения.
В строках 24-26 определяется деструктор ~Cat(). Эта функция не выполняет никаких действий, но коль вы объявляете ее в классе, нужно обязательно включить и ее определение.