- •Обзорные лекции по курсу
- •1.Концепции и методология объектно-ориентированного программирования
- •2.Классы. Конструкторы и деструкторы
- •Void set_a(int num);
- •Void AnyClass::get_a()
- •Int AnyClass::set_a(int num)
- •Конструкторы и деструкторы. Список инициализации элементов
- •Void Show();
- •Int main()
- •Void Show();
- •Int main()
- •Статические члены класса
- •3. Простое и множественное наследование
- •Int GetX(){return X;}
- •Int GetY(){return у;}
- •Int GetX(){return X;}
- •Int GetY(){return y;}
- •Void ShowX(){cout « GetX() « ' ' ;}
- •Void ShowY(){cout « GetY()« ' ';}
- •OutCoord(int _x, int _y): Coord(_x, _y){}
- •Int GetX() {return X;}
- •Int GetY(){return y;}
- •Void ShowX(){cout « GetX() « ' ';}
- •Void ShowY(){cout « GetY()« ' ';}
- •Void ShowX(){cout « Coord::GetX() « ' ';}
- •PrintMsg(int _x, int _y, char* msg): Coord(_x, _y), SaveMsg(msg){}
- •Void set_a(int num);
- •Void AnyClass::get_a()
- •Int AnyClass::set_a(int num)
- •Void Show();
- •Int main()
- •Int GetX() {return X;}
- •PrintMsg(int _x, int _y, char* msg): Coord(_x, _y), SaveMsg(msg){}
- •Упражнения для самопроверки
Void set_a(int num);
};
Хотя функции get_a() и set_a() и объявлены в классе AnyClass, они еще не определены. Для определения функции-члена нужно связать имя класса, частью которого является функция-член, с именем функции. Это достигается путем написания имени функции вслед за именем класса с двумя двоеточиями. Операцию, записываемую в виде двух двоеточий, называют операцией расширения области видимости. Для задания функции-члена используется следующая общая форма:
<Тип> <имя_класса>: : <имя_функции>(<список_параметров>)
{
//тело функции
}
Ниже приведены примеры определения функций-членов get_a() и set_a():
Void AnyClass::get_a()
{
return a;
}
Int AnyClass::set_a(int num)
{
a = num;
}
Объявление класса AnyClass не приводит к созданию объектов типа AnyClass. Чтобы создать объект соответствующего класса, нужно просто использовать имя класса как спецификатор типа данных. Например,
AnyClass ob1, оb2;
После того, как объект класса создан, можно обращаться к открытым членам класса, используя операцию "точка".
Например,
obl.set_a(10) ;
ob2.set_a(37) ;
Эти операторы устанавливают значения переменной а в объектах оb1 и оb2. Каждый объект содержит собственную копию всех данных, объявленных в классе. Это значит, что значение переменной а в оb1 отлично от значения этой переменной в оb2.
Создавая некоторый объект, его необходимо проинициализировать. Для этой цели C++ предоставляет функцию-член, которая называется конструктором. Конструктор класса вызывается всякий раз, когда создается объект его класса. Конструктор имеет то же имя, что и класс, членом которого он является, и не имеет возвращаемого значения. Например,
#include <iostream.h>
class AnyClass
{
int var;
public:
AnyClass(); //Конструктор
Void Show();
};
AnyClass:: AnyClass()
{
cout « 'В конcтрукторе\n';
var = 0;
}
void
AnyClass::Show()
{
cout « var;
}
Int main()
{
AnyClass ob;
ob.Show();
//...
return 0;
}
В этом примере конструктор класса AnyClass выводит сообщение на экран и инициализирует значение закрытой переменной класса var.
Программист не должен писать код, вызывающий конструктор класса. Всю необходимую работу выполняет компилятор. Конструктор вызывается тогда, когда создается объект его класса. Объект, в свою очередь, создается при выполнении оператора, объявляющего этот объект. Таким образом, в C++ оператор объявления переменной является выполнимым оператором.
Для глобальных объектов конструктор вызывается тогда, когда начинается выполнение программы. Для локальных объектов конструктор вызывается всякий раз при выполнении оператора, объявляющего переменную.
Функцией-членом, выполняющей действия, обратные конструктору, является деструктор. Эта функция-член вызывается при удалении объекта. Деструктор обычно выполняет работу по освобождению памяти, занятой объектом. Он имеет то же имя, что и класс, которому он принадлежит, с предшествующим символом ~ и не имеет возвращаемого значения. Рассмотрим пример класса, содержащего деструктор:
#include <iostream.h>
class AnyClass
{
int var;
public:
AnyClass(); //Конструктор
~AnyClass(); //Деструктор
void Show();
};
AnyClass::AnyClass()
{
cout « "Мы в конструкторе\n";
var = 0;
}
AnyClass::~ AnyClass()
{
cout « "Мы в деструкторе\n";
}
void
AnyClass::Show()
{
cout « var « "\n";
}
int main()
{
AnyClass ob;
ob.Show();
//...
return 0;
}
Деструктор класса вызывается в момент удаления объекта. Это означает, что для глобальных объектов он вызывается при завершении программы, а для локальных — когда они выходят из области видимости. Заметим, что невозможно получить указатели на конструктор и деструктор.
Члены класса могут быть объявлены с модификатором static. Статический член класса может рассматриваться как глобальная переменная или функция, доступная только в пределах области класса.
Данное-член класса, определенное с модификатором static, разделяется всеми представителями этого класса, так как на самом деле существует только один экземпляр этой переменной. На самом деле, память под статические данные-члены выделяется, даже если нет никаких представителей класса. Поэтому класс должен не только объявлять статические данные-члены, но и определять их.
Например:
class AnyClass
{
public:
AnyClass();
//объявление статического данного-члена
static int count;
};
//определение статического данного-члена
int AnyClass::count = 0;
Заметим, что хотя к статическим данным-членам, объявленным в разделе public класса, можно обращаться с помощью конструкции, использующих имя объекта:
<объект>.<данное-_член>
или
<указатель_на_объект> - > <данное_член>,
лучше использовать форму вызова
<имя_класса>: : <данное -_член>,
которая является более правильной, так как отражает тот факт, что соответствующее данное-член является единственным для всего класса. Если статические данные-члены объявлены как закрытые, то доступ к ним можно получить с помощью обычных функций-членов. Доступ к статическим данным-членам с помощью обычных функций-членов ничем не отличается от доступа к другим данным-членам, но для этого необходимо создать хотя бы один объект данного класса. В связи со сказанным выше, можно дать следующие рекомендации:
• применяйте статически данные-члены для совместного использования данных несколькими объектами класса;
• ограничьте доступ к статическим данным-членам, объявив их в разделе protected или private.
Рассмотрим пример использования статического данного-члена класса:
#include <iostream.h>
class T
{
public:
T(){ObCount++;}
~T(){ObCount--;}
static int ObCount;
//...
private:
int x;
};
int Т::ObCount = 0;
int main()
{
T* pOb = new Т[5];
cout « " Имеется " « Т::ObCount « " объектов типа Т\n";
delete[] рОb;
return 0;
}
В этом примере статическое данное-член просто ведет учет созданных объектов. При выполнении программа выводит на экран:
Имеется 5 объектов типа Т
Язык C++ позволяет классу наследовать данные-члены и функции-члены одного или нескольких других классов. При этом новый класс называют производным классом (или классом-потомком). Класс, элементы которого наследуются, называется базовым классом (родительским классом или классом-предком) для своего производного класса. Наследование дает возможность некоторые общие черты поведения классов абстрагировать в одном базовом классе. Производные классы, наследуя это общее поведение, могут его несколько видоизменять, переопределяя некоторые функции-члены базового класса, или дополнять, вводя новые данные-члены и функции-члены. Таким образом, определение производного класса значительно сокращается, поскольку нужно определить только отличающие его от производных классов черты поведения.
Синтаксис объявления производного класса имеет следующий вид:
class Basel //Базовый класс
{
//...
};
class Base2 //Базовой класс
{
//...
};
class BaseN //Базовый класс
{
//...
};
//Производный класс
class Derived : <спецификатор_доступа> Basel,
<спецификатор_доступа> Base2,
…,
<спецификатор_доступа> BaseN
{
//...
};
Здесь <спецификатор_доступа> — это public, protected или private; он не является обязательным и по умолчанию принимает значение private для классов и public для структур. Спецификатор доступа при наследовании определяет уровень доступа к элементам базового класса, который получают элементы производного класса. В приведенной ниже таблице описаны возможные варианты наследуемого доступа.
Таблица 6.2. Наследуемый доступ
Спецификатор наследуемого доступа |
Доступ в базовом классе |
Доступ в производном классе |
public
|
Public Protected Private |
Public Protected Недоступны |
protected
|
Public Protected Private |
Protected Protected Недоступны |
private
|
Public Protected Private |
Private Private Недоступны |
Если у производного класса имеется несколько базовых классов, то говорят о множественном наследовании. Множественное наследование позволяет сочетать в одном производном классе свойства и поведение нескольких классов.
Следующий пример демонстрирует множественное наследование.
#include <iostream.h>
#include <conio.h>
#include <string.h>
class Coord
{
int x, y;
public:
Coord(int _x, int _y){x = _x; у = _y;}