Базовые и производные классы

Простое наследование.

Обычно классы не существуют сами по себе. Как правило, программа ра­ботает с несколькими различными, но связанными структурами данных.

В С++ решением проблемы "похожие, но различные" является разрешение классам наследовать характеристики и поведение от одного или нескольких классов. Этот интуитивный переход и составляет, пожалуй, наибольшее раз­личие между языками С и С++.

Одной из важнейших концепций языка С++ является возможность насле­дования свойств одного класса другим — это простое наследование, что можно показать так: A<-B (класс В наследник класса А). Класс, на основе которого строится другой класс, называется базовым (base) классом (предок, родитель). Построенный на его основе новый класс называется производным (derived) (по­томок). Из одного класса могут порождаться многие классы:

Базовые и производные классы - student2.ru Базовый класс

Базовые и производные классы - student2.ru

Производный класс 1 . . . Производный класс N

Производный класс сам может быть базовым для других классов, что по­рождает иерархию классов:

Базовый класс А Базовые и производные классы - student2.ru

Базовые и производные классы - student2.ru Производный класс В Базовый класс для С

Производный класс C Класс С — производный от А и В

Контроль доступа в классах.

Производный класс наследует из базового данные-элементы и функции-элементы в зависимости от режимов доступа в базовом классе и модификато­ров доступа при наследовании.

Шаблон описания производного класса имеет вид:

class Производный_класс :модификатор_доступа Базовый_класс

{

тело производного класса;

} объекты производного класса (не обязательно) ;

например:

class Point :public Location { . . .};

Как известно, в С++ элементы класса могут иметь один из трех режимов доступа: public (общий), private (закрытый), protected (защищенный). Общие элементы могут быть доступны другим функциям программы. Закрытые эле­менты доступны только функциям-элементам или функциям-друзьям класса. Защищенные элементы также доступны только тем же функциям.

Модификаторы доступа (public, protected, private (по умолчанию)) в производном классе используются для изменения прав доступа к наследуемым элементам базового класса в соответствии с установленными правилами:

Режим доступа в базовом классе: Модификатор доступа в производном классе: Права доступа к элементам ба­зового класса из производного:
private protected public   public нет доступа protected public
private protected public   protected нет доступа protected protected
private protected public   private нет доступа private private


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

При создании новых классов необходимо ясно понимать взаимосвязь между базовыми и производными классами и влияние модификатора доступа. С++ позволяет изменять права доступа без раскрытия данных перед методами, не принадлежащими к данному семейству классов или друзьям. То есть эле­менты базового класса, которые предполагается использовать в производном классе, должны быть либо public, либо protected.

Модификатор доступа public не изменяет уровня доступа, то есть protected элементы базового класса остаются protected и в производном клас­се, что гарантирует их недоступность везде, кроме других производных классов, объявленных с модификатором public, и функций-друзей.

Наследование типа private используется в классах по умолчанию, являясь одновременно и наиболее распространенным способом наследования. Таким образом, получаем довольно редкую ситуацию, при которой то, что задано по умолчанию является и нормой.

Элементы класса protected — это нечто среднее между открытыми и за­крытыми элементами.

При выборе режима доступа к элементам действуют следующие правила:

— закрытые (private) элементы доступны только в классе, в котором они объявлены и функциям-друзьям;

— защищенные (protected) элементы доступны элементам их собствен­ного класса и всем элементам производного класса и функциям-друзьям, но только в объектах производного класса. Вне класса защищенные элементы недоступны;

— открытые (public) элементы доступны во всей программе.

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

Пример 25.

Рассмотрим следующий класс.

class X

{ private:

int A; // доступен только элементам этого класса

void fa(); // доступен только элементам этого класса

protected:

int B; // доступен элементам этого класса и производного

void fb(); // доступен элементам этого класса и производного

public:

int C; // доступен всем, кто его использует

void fc(); // доступен всем, кто его использует

};

Внешние операторы не могут вызывать методы fa, fb и использовать эле­менты А и В.

Когда базовый класс объявлен private для производного класса, то это су­щественно влияет на наследуемые элементы.

Пример 26.

Рассмотрим следующие классы.

class Base

{ protected: int x; // защищенный элемент х

public: int y; // общий элемент y

};

class Derived: private Base // класс Base закрыт!

{ public: void f();

};

Метод f() класса Derived имеет доступ к элементам х и у, наследованным от класса Base. Но поскольку Base объявлен закрытым классом для Derived, статус х и у изменился на private в классе Derived.

Добавим класс Derived1 производный от класса Derived.

class Derived1: public Derived

{ public: void f1();

};

В этом классе метод void f1() не имеет доступа к элементам х и у, не­смотря на то, что в классе они имели статус protected и public .

Пример 27.

Рассмотрим варианты наследования режимов доступа к элементам классов при простом наследовании по иерархии классов X <- Y <- Z.

#include<iostream.h>

#include<conio.h>

class X // базовый класс X для классов Y, Z

{ protected: int i, j; // защищенные переменные доступные в Y, Z

public: // прототипы открытых методов:

void get_ij(); // ввод чисел

void put_ij(); // вывод чисел

};

// Класс Y производный от X с сохранением режимов доступа к элементам Х:

class Y: public X

{ int k; // private по умолчанию в классе Y

public: // прототипы открытых методов класса Y:

int get_k(); // возврат числа k

void make_k(); // вычисление результата k

};

// Класс Z производный от Y и Х имеет доступ к переменным i, j класса Х

// и нет доступа к закрытой переменной k класса Y:

class Z: public Y

{ public:

void f(); // прототип функции, изменяющей i, j

};

// Описание методов классов:

void X::get_ij() // функция ввода чисел i, j

{ cout<<"Введите два числа: ";

cin >> i >> j;

}

void X::put_ij() // функция ввода чисел i, j

{ cout<<"i="<<i<<" j="<<j<<endl;

}

int Y::get_k() // функция возврата числа k

{ return k;

}

void Y::make_k() // функция вычисления числа k

{ k=i*j;

}

void Z::f() // функция, изменяющяя i, j класса Х

{ i=2; j=3;

}

void main() // главная функция

{ clrscr(); // чистка экрана

Y var1; // создан объект var1 класса Y

Z var2; // создан объект var2 класса Z

var1.get_ij(); // ввод чисел i, j

var1.put_ij(); // вывод чисел i, j

var1.make_k(); // вычисление числа k=i*j

cout<<"k=i*j="<<var1.get_k(); // вывод числа k=i*j

cout<<'\n'; // переход на новую строку экрана

var2.f(); // изменение элементов i, j класса Х в классе Z

var2.put_ij(); // вывод чисел i, j

var2.make_k(); // вычисление числа k=i*j

cout<<"k=i*j="<<var2.get_k(); // вывод результата k=i*j

cout<<'\n';

getch();

}

Результаты программы:

Введите два числа: 3 7

i=3 j=7

k=i*j=21

i=2 j=3

k=i*j=6

Если в данной программе заменить модификатор доступа в классе Y при наследовании класса X на private, то функция void Z::f() не будет иметь право доступа к переменным i, j.

Наши рекомендации