- •ПРОГРАММИРОВАНИЕ НА ЯЗЫКАХ С И С++
- •СОДЕРЖАНИЕ
- •ВВЕДЕНИЕ
- •1. ЛЕКСИКА ЯЗЫКА C
- •1.1. РАЗДЕЛИТЕЛИ
- •1.2. КОММЕНТАРИИ
- •2. ДАННЫЕ И ОПЕРАЦИИ
- •2.1. БАЗОВЫЕ ТИПЫ ДАННЫХ
- •Наименование
- •Диапазон
- •2.2. КОНСТАНТЫ
- •Целые константы
- •Константы с плавающей точкой
- •Символьные константы
- •2.3. ОПРЕДЕЛЕНИЕ ПЕРЕМЕННЫХ И ТИПИЗИРОВАННЫХ КОНСТАНТ
- •2.4. ОДНОМЕРНЫЙ МАССИВ
- •2.5. СТРОКИ, СТРОКОВЫЕ КОНСТАНТЫ
- •2.6. УКАЗАТЕЛИ
- •2.7. ПЕРЕИМЕНОВАНИЯ ТИПОВ
- •2.8. СТРУКТУРЫ
- •2.9. МАССИВЫ СТРУКТУР
- •2.10. МНОГОМЕРНЫЕ МАССИВЫ
- •2.11. ОБЪЕДИНЕНИЕ
- •2.12. ПЕРЕЧИСЛЕНИЕ
- •2.13. БИТОВОЕ ПОЛЕ ( ЗАПИСЬ )
- •3. ОПЕРАЦИИ И ВЫРАЖЕНИЯ
- •3.1. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
- •3.2. ОПЕРАЦИИ СРАВНЕНИЯ
- •3.3. ЛОГИЧЕСКИЕ ОПЕРАЦИИ
- •3.4. ПОБИТОВЫЕ (ПОРАЗРЯДНЫЕ) ОПЕРАЦИИ
- •3.5. ОПЕРАЦИИ ПРИСВАИВАНИЯ
- •3.6. АДРЕСНЫЕ ОПЕРАЦИИ
- •3.7. ДОСТУП К ПОЛЯМ СТРУКТУР, ОБЪЕДИНЕНИЙ И ЗАПИСЕЙ
- •3.8. ДРУГИЕ ОПЕРАЦИИ
- •3.9. ПОРЯДОК ВЫПОЛНЕНИЯ ОПЕРАЦИЙ В ВЫРАЖЕНИИ
- •4. ОПЕРАТОРЫ
- •4.1. ОПЕРАТОР-ВЫРАЖЕНИЕ
- •4.2. ПУСТОЙ ОПЕРАТОР
- •4.3. СОСТАВНОЙ ОПЕРАТОР
- •4.4. УСЛОВНЫЕ ОПЕРАТОРЫ
- •4.5. ОПЕРАТОРЫ ЦИКЛА
- •while( выражение) оператор;
- •do оператор while ( выражение );
- •for( выражение1; выражение2; выражение3 ) оператор;
- •4.6. ОПЕРАТОРЫ ПЕРЕХОДА
- •break;
- •switch
- •continue;
- •goto метка;
- •5. ФУНКЦИИ. КЛАССЫ ПАМЯТИ
- •5.1. ФУНКЦИИ
- •5.2. КЛАССЫ ПАМЯТИ
- •класс памяти
- •ключевое слово
- •существование
- •видимость
- •5.3. ВОЗВРАЩЕНИЕ ЗНАЧЕНИЙ
- •5.4. АРГУМЕНТЫ ФУНКЦИИ
- •6. ФУНКЦИИ ВВОДА С КЛАВИАТУРЫ И ВЫВОДА НА ДИСПЛЕЙ
- •6.1. ФОРМАТИРОВАННЫЙ ВЫВОД
- •6.2. ФОРМАТИРОВАННЫЙ ВВОД
- •6.3. ВВОД СТРОКИ
- •6.4. ВЫВОД СТРОКИ
- •6.5. ВВОД СИМВОЛА
- •6.6. ВЫВОД СИМВОЛА
- •7. ПРЕПРОЦЕССОР
- •7.1. ВКЛЮЧЕНИЕ ФАЙЛОВ
- •7.2. ВЫПОЛНЕНИЕ ПОДСТАНОВОК
- •7.3. УСЛОВНАЯ КОМПИЛЯЦИЯ
- •8. ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ, СПИСКИ
- •8.1. ВЫДЕЛЕНИЕ И ОСВОБОЖДЕНИЕ ДИНАМИЧЕСКОЙ ПАМЯТИ
- •8.2. СПИСКИ
- •Рис.1. Структура однонаправленного линейного списка
- •9. ФАЙЛЫ
- •9.1. ОТКРЫТИЕ И ЗАКРЫТИЕ ФАЙЛОВ
- •9.2. ЧТЕНИЕ И ЗАПИСЬ В ФАЙЛ
- •9.3. ПРОИЗВОЛЬНЫЙ ДОСТУП К ФАЙЛУ
- •10. ЛАБОРАТОРНЫЕ РАБОТЫ
- •10.1. МАССИВЫ И СИМВОЛЬНЫЕ СТРОКИ
- •Цель работы
- •Пример выполнения работы
- •Варианты заданий
- •10.2. ФУНКЦИИ
- •Цель работы
- •Пример выполнения работы
- •Варианты заданий
- •10.3. СТРУКТУРЫ И МАССИВЫ СТРУКТУР
- •Цель работы
- •Пример выполнения работы
- •Варианты заданий
- •10.4. ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ, СПИСКИ
- •Цель работы
- •Пример выполнения работы
- •Варианты заданий
- •10.5. РАБОТА С ФАЙЛАМИ
- •Цель работы
- •Пример выполнения работы
- •Варианты заданий
- •11. ВВЕДЕНИЕ В C++
- •12. КЛАССЫ
- •12.1. ОБЩИЕ ПОЛОЖЕНИЯ
- •12.2. КОНСТРУКТОРЫ И ДЕСТРУКТОРЫ
- •12.3. СОЗДАНИЕ И ВЫЗОВ ОБЪЕКТОВ
- •12.4. ДРУЖЕСТВЕННЫЕ ФУНКЦИИ
- •12.5. ПЕPЕГPУЖЕННЫЕ ОПЕPАЦИИ И ФУНКЦИИ
- •12.6. ПАРАМЕТРИЗИРОВАННЫЕ КЛАССЫ
- •12.7. ПРЕОБРАЗОВАНИЕ ТИПОВ
- •12.8. СТАТИЧЕСКИЕ КОМПОНЕНТЫ КЛАССА
- •13. ПРОИЗВОДНЫЕ КЛАССЫ, НАСЛЕДОВАНИЕ
- •13.1. ИЕРАРХИЯ КЛАССОВ
- •13.2. ДОСТУП К НАСЛЕДУЕМЫМ КОМПОНЕНТАМ
- •Доступ в базовом классе
- •Модификатор доступа
- •14. ВИРТУАЛЬНЫЕ ФУНКЦИИ
- •14.1. ОБЩИЕ ПОЛОЖЕНИЯ
- •14.2. ВИРТУАЛЬНЫЕ ДЕСТРУКТОРЫ
- •14.3. АБСТРАКТНЫЕ КЛАССЫ
- •15.1. ВЫВОД ДАННЫХ
- •15.2. ВВОД ДАННЫХ
- •15.3. ВВОД-ВЫВОД ДАННЫХ, ОПРЕДЕЛЕННЫХ ПОЛЬЗОВАТЕЛЕМ
- •15.4. РАБОТА С ДИСКОВЫМИ ФАЙЛАМИ
- •15.5. ОБРАБОТКА ИСКЛЮЧЕНИЙ
- •ЛИТЕРАТУРА
– 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 Калачев Д.П., Лутай В.Н.