Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабник по C.pdf
Скачиваний:
74
Добавлен:
01.06.2015
Размер:
876.9 Кб
Скачать

– 39 –

12. КЛАССЫ

12.1.ОБЩИЕ ПОЛОЖЕНИЯ

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

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

class Complex{ // 1

double re, im; // 2 public: // 3

Complex(double r, double i){re=r; im=i;} // 4 Complex(){re=0; im=0;} // 5

double getR(){ return re;} // 6 double getI(){ return im;} // 7

void setC(double a, double b){re=a; im=b;} // 8 }; // 9

void main(){

Complex m1(4,6), m2, m3[6]; // 10 m2.setC(5, 8); // 11 cout<<m2.getR()<<m2.getI()<<endl; // 12

}

Кратко рассмотрим составляющие программы. Со строки 1 начинается определение класса. В строке 2 объявлены два поля – данные класса. Ключевое слово public в строке 3 определяет соответствующий доступ к полям класса. В строках 4 и 5 приведены конструкторы класса – функции, создающие поля класса. В строках 6 – 8 записаны методы: первые два возвращают значения реальной и мнимой частей комплексного числа соответственно, третий устанавливает их значения. Девятой строкой заканчивается определение класса. В 10 строке объявляются 3 объекта (экземпляра) класса: переменные m1 и m2 (m1 инициализируется при объявлении) и массив m3. Оператор в 11 строке присваивает полям переменной m2 соответствующие значения, в 12 строке производится вывод этих значений на экран.

Определение класса имеет следующий вид:

тип_класса имя_класса { поля, методы };

Тип класса может быть задан одним из 3-х атрибутов: class, struct, union. Имя класса становится идентификатором нового типа данных. Поля определяются с помощью базовых типов, методы записываются как обычные функции. Поля и методы, включенные в класс, называются его компонентами. Совокупность методов далее будем называть интерфейсом класса.

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

[тип] имя_класса:: имя_компоненты,

где символ :: называется оператором принадлежности. Если перед этим оператором нет имени класса, то задаваемое имя определяется как глобальное, известное за пределами классов.

В программе наряду с определением класса допускается его объявление вида:

тип_класса имя_класса;

© 1998 Калачев Д.П., Лутай В.Н.

– 40 –

Примеры

class X; // Объявление класса Х class Y { // Определение класса Y X m1;

...

}

class X{ // Определение класса Х

...

}

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

12.2. КОНСТРУКТОРЫ И ДЕСТРУКТОРЫ

Конструктор – функция, создающая поля класса и, возможно, их инициализирующая. Приведем пример достаточно сложного класса String.

class String{

// 1

char *charPtr;

// 2

int length;

// 3

public:

// 4

String(int size=80);

// 5

String(char *text);

// 6

String(String& Other_String);

// 7

~String(){delete charPtr;};

// 8

int GetLen(void);

// 9

void Show(void);

//10

};

 

String::String (int size){

 

length=size;

 

charPtr=new char[length + 1];

 

*charPtr =‘\0’;

 

}

 

String::String(char *text) {

 

length=strlen(text);

 

char_ptr=new char [length+1];

 

strcpy(charPtr, text);

 

}

 

String::String(String& Other_String) { length=Other_String.length; char_ptr=new char[length+1]; strcpy(charPtr, Other_String.charPtr);

}

Первый конструктор класса String (строка 5) создает строку с заданной длиной (по умолчанию 80). Второй конструктор создает поле для символьной строки и инициализирует его текстом, передаваемым в качестве аргумента. Начальное значение для поля класса здесь задается как аргумент конструктора. Наряду с этим начальные значения могут задаваться в виде списка инициализации, который отделяется от списка аргументов конструктора двоеточием. Второй способ инициализации иногда предпочтительнее, т. к. в этот список можно включать компоненты другого класса. Третий конструктор (строка 7) копирует уже имеющуюся строку, во вновь создаваемую, и называется конструктором копирования.

Пример использования всех трех конструкторов следует ниже: void main() {

String Astring(" Инкапсуляция"); String Bstring(Astring);

String Cstring(25);

. . .

}

© 1998 Калачев Д.П., Лутай В.Н.

41 –

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

Перечислим основные свойства и правила использования конструкторов:

конструктор имеет то же имя, что и класс, в котором он объявляется;

конструктор не имеет типа возвращаемого значения;

конструктор может иметь аргументы, заданные по умолчанию; эти аргументы не перечисляются при вызове конструктора;

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

конструктор вызывается автоматически только при создании объекта;

конструктор класса X может иметь аргумент типа X& (ссылку на объект типа X), но не аргумент типа X.

конструктор, заданный в виде X::X(X&), называется конструктором копирования; он подразумевает использование свободной памяти.

Деструктор разрушает поля класса; его можно узнать по символу ~. Деструктор не имеет аргументов и не возвращает значения. В классе может быть только один деструктор. Если деструктор не задан в программе, то он автоматически генерируется компилятором. Деструктор автоматически вызывается при разрушении объекта за исключением случая, когда массив объектов создается оператором new. В последнем случае для разрушения объектов должен быть использован оператор delete для каждого элемента массива. (В классах Complex и String деструкторы вызываются автоматически).

12.3. СОЗДАНИЕ И ВЫЗОВ ОБЪЕКТОВ

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

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

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

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

объекты в динамической памяти. Создаются оператором new и разрушаются операто-

ром delete;

объекты-компоненты классов. Создаются при построении объекта класса, в котором они описаны, и разрушаются при разрушении этого объекта.

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

Для создания компонентов-массивов необходимо иметь в классе конструктор без аргументов; в противном случае использование [] для обозначения компонента массива будет ошибочным. Если в классе вообще нет конструкторов, то компилятор создаст нужный конструктор автоматически; если же конструкторы есть, то к ним надо прибавить такой конструктор.

Локальные поля не могут инициализироваться при их объявлении в классе.

Обращение к объектам-компонентам класса при разрешенном доступе может быть двоя-

ким: имя_объекта.имя_компоненты или имя_объекта->имя_компоненты. Такой вид об-

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

Доступ к компоненту определяется наличием (или отсутствием) в определении класса атрибутов доступа public, private, protected. Атрибут public определяет все следующие за ним компоненты как открытые, к которым разрешен прямой доступ из любой точки

© 1998 Калачев Д.П., Лутай В.Н.

– 42 –

программы; private запрещает прямой доступ к соответствующим компонентам извне (закрытые компоненты). В классе типа class все компоненты по умолчанию private, в классах типа struct и union public. Каждый атрибут, записанный в классе, отменяет действие предыдущего. Смысл употребления слова protected (защищенный) описан ниже. В классе типа union разрешено использовать только открытые компоненты.

Принятый способ использования атрибутов: данные объявляются типа private (protected в базовых классах ), методы – типа public.

В классе Complex поля закрыты для прямого доступа (private по умолчанию). Поэтому попытка обратиться к полю напрямую, например m2.re=6, обречена на неудачу; доступ к полям возможен только через методы getI, getR, setC.

Подробнее рассмотрим функцию getR. Реальную часть какой комплексной переменной возвращает оператор return ? Для ответа на вопрос запишем аргумент этого оператора в явной форме:

double getR(){return (*this).re;}

this – скрытый указатель на объект, по отношению к которому вызывается метод. Говорят, что this вызывает самое себя. Выражение return (*this) означает возвращение значения объекта, с которым будет работать данная функция, т.е. переменной m2. Указатель this работает только в методах; объявлять его не надо.

Добавим к классу Complex метод сложения двух комплексных чисел.

Complex addC(Complex a){return Complex (a.re+re, a.im+im);}

Применяется он следующим образом: m4=m1.addC(m2); cout<<m4.getR()<<endl<<m4.getI()<<endl;

Вклассах типа struct и union проблем с доступом нет, при условии, естественно, что

вопределении класса нет закрытых компонент.

ВС++ введены два оператора, позволяющие обратиться к указателю в классе: .* и ->*.

Доступ к вложенному классу зависит от того, с каким атрибутом он объявлен во внешнем классе. Пример:

class Class_1 { class Class_2{

. . .

};

public:

. . .

};

Здесь Class_2 является вложенным; так как по умолчанию он имеет атрибут private, то его компоненты недоступны за пределами класса Class_1; к ним имеют доступ только компоненты последнего (доступ может быть прямым или через интерфейс в зависимости от атрибутов компонент в классе Class_2).

Заметим, что функция-компонент класса может быть объявлена так, что ей разрешено только читать значения компонентов-полей и не разрешено их изменять. Такие функции объявляются со спецификатором const:

тип имя_функции const (список параметров);

12.4. ДРУЖЕСТВЕННЫЕ ФУНКЦИИ

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

friend тип имя (параметры);

Дружественные функции рекомендуется использовать в следующих случаях:

при необходимости упростить форму обращения к данным класса: не будучи компонентом класса, дружественная функция не требует соответствующей формы вызова. Например, для класса Complex можно определить функции getR и getI как

©1998 Калачев Д.П., Лутай В.Н.