Полиморфизм: виртуальные методы класса




Семинар 3. Наследование и полиморфизм

Для эффективной разработки программ удобно использовать иерархическое упорядочение понятий и объектов. Такое упорядочение позволяет легче справляться со сложностью разрабатываемых программ, сделать логику их работы более простой и понятной. Иерархия понятий реализуется в виде древовидной структуры, в основе которой лежит наиболее общее понятие. В языке С++ подобная структура реализуется через механизм производных классов, наследующих свойства базовых классов.

Производные классы – развитие классов, определенных ранее, имеющие доступ к protected и public части базового класса. Производные классы наследуют все свойства базовых классов, при этом некоторые свойства могут быть запрещены к наследованию, другие – можно изменить при наследовании, а третьи – могут быть добавлены к свойствам базового класса.

Права доступа к членам базового класса

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

class Base {

private:

..............

protected:

..............

public:

..............

 

};

class Derived: public Base { // здесь может применяться private и protected

public:

..............

};

 

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

Таблица прав доступа к членам базового класса в производном классе

Модификатор доступа, указанный при наследовании Право доступа в базовом классе Наследуемое право доступа в производном классе
private private protected public не доступен private private
protected private protected public не доступен protected protected
  public private protected public не доступен protected public

Дружественные функции и классы

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

В тех случаях, когда классы тесно «взаимодействуют» друг с другом (то есть, когда объекты одного класса являются аргументами членов функции другого класса) бывает удобно разрешить доступ таким классам к приватным и защищенным (private, protected) членам этих классов. Для этого в теле класса, к членам которого открывается доступ, нужно описать класс, получающий доступ, с ключевым словом friend.

Отметим, что помимо воли автора этого класса получить доступ к его защищенным полям и методам – нельзя, то есть, чтобы этот доступ получить – в нем самом нужно сделать дополнительное объявление дружественных классов или функций.

#include <ostream>

 

class CVector {

private:

int len; // размерность вектора

float *ptr; // указатель на область памяти, содержащую элементы вектора.

private:

...

// дружественный оператор вывода:

friend ostream& operator<< (ostream& os, const CVector &v);

friend class CMatrix; // дружественный класс

...

};

 

ostream& operator<< (ostream& os, const CVector &v)

{

for (int n=0; n < v.len; n++) {

os << "[" << n << "] " << v.ptr[n] << endl;

}

return os;

}

 

class CMatrix {

private:

int cx, cy; //размерности матрицы

float *ptr; // указатель на память, содержащую элементы матрицы

 

public:

...

// оператор умножения матрицы на вектор:

CVector& operator * (CVector &v);

...

};


Перегрузка при наследовании

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

class Base {

...

public:

void mf(void) { cout << 1; }

};

class Derived: public Base {

...

public:

void mf(void) { cout << 2; }

};

 

Base x;

Derived y;

...

x.mf(); // 1, вызывается функция базового класса Base

y.mf(); // 2, вызывается функция производного класса Derived;

Полиморфизм: виртуальные методы класса

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

class Base {

...

public:

virtual void mf(void) { cout << 1; }

void mfstd(void) { cout << 10; }

};

class Derived: public Base {

...

public:

void mf(void) { cout << 2; }

void mfstd(void) { cout << 20; }

};

 

Base x;

Derived y;

 

Base *px = &x;

Derived *py = &y;

Base *pxy = &y;

 

px->mf(); // 1, вызывается функция базового класса Base

 

py->mf(); // 2, вызывается функция производного класса Derived;

 

pxy->mf(); // 2, полиморфный вызов: мы не знаем, что работаем с Derived,

// так как располагаем указателем на Base, но метод вызывается

// из Derived

 

pxy->mfstd(); // 10, так как вызов не полиморфный – в базовом классе функция

// не виртуальная.

Абстрактные классы

Абстрактные классы содержат, по крайней мере, одну чистую виртуальную функцию. В программе не могут быть определены объекты абстрактных классов или ссылки на них, но можно определить и использовать указатели на объекты абстрактных классов.

class CPoint {

public:

int x,y;

public:

CPoint (int nx=0, int ny=0): x(nx), y(ny) {} // конструктор

// конструктор копирования

CPoint (const CPoint& src): x(src.x), y(src.y) {}

};

 

class CShape { // абстрактный класс, так как в нем есть чистая функция

protected:

CPoint center; // центр объекта

public:

CShape (const CPoint &nс): center(nc) {} // конструктор

// перемещение фигуры, невиртуальная функция:

void MoveTo(int nx=0, int ny=0) { center.x = nx; center.y = ny; }

 

// чистая функция (pure function) для подсчета площади:

virtual double Square() = 0;

};

 

class CCircle: public CShape {

public:

int radius; // радиус круга

CCircle(int nx=0, int ny=0, int rad=0):

CShape(CPoint(nx,ny)), radius(rad) {}

public:

double Square() { return double(3.14159) * radius * radius; }

};

 

class CQuadrat: public CShape {

public:

int side; // сторона квадрата

CQuadrat (int nx=0, int ny=0, int ns=0):

CShape(CPoint(nx,ny)), side(ns) {}

public:

double Square() { return side * side; }

};

 

CCircle c1(1,2,1), c2(2,3,8);

CQuadrat q1(-2,0,3), q2(-2,0,5);

CShape* shapes[4] = { &c1, &q1, &c2, &q2 };

 

// подсчет площади всех фигур:

 

double s = 0;

for (int i=0; i < 4; i++) {

s += shapes[i]->Square();

}

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2018-01-08 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: