3.1. Простое наследование.
Простое наследование описывает родство между двумя классами: один из которых наследует второму. Класс, нахо- дящийся на вершине иерархии, называется базовым классом. Прочие классы называются производными классами. Из одного класса могут выводится многие классы, но даже в этом случае подобный вид взаимосвязи остается простым наследованием.
Базовый класс часто называют предком, а производный класс — потомком.
Производный класс наследует из базового данные-члены и функции-члены, но не конструкторы и деструкторы.
class Tbase { //Базовый класс
private:
int count;
public:
TBase() {count = 0;}
void SetCount(int n) {count = n;}
int GetCount(void) {return count;}
};
class TDerived: public TBase { //Производный класс
public:
TDerived(): Tbase() {}
void ChangeCount(int n) {SetCount(GetCount() + n);}
};
Производный класс назван TDerived. За именем сле-дует двоеточие и одно из ключевых слов — public, protected, private. После этих элементов имя базового класса TBase. Открытые члены открытого класса остаются открытыми в производном классе:
class TDerived: public TBase {}
Все закрытые члены Tbase остаются закрытыми и в Tderived нельзя получить к ним доступ. Можно объявить базовый класс закрытым:
class TDerived: private TBase {}
Все открытые члены в базовом классе становятся закры-тыми в TDerived. Последующие наследования TDerived не смо-гут получить доступа в членам TBase. Обычно в производном классе есть конструктор, если он был в базовом. Он должен вызывать конструктор базового класса:
TDerived(): TBase() {}
Кроме этого в нем могут выполняться и другие действия, например:
TDerived():TBase() {cout <<» I am being initialized» <<endl;}
Можно реализовать объявленный конструктор отдельно:
TDerived::TDerived()
: TBase()
{ // операторы
}
Производный класс наследует count из базового класса, но так как он закрыт в TBase доступ к нему возможен только через функции-члены класса TBase. В производном классе можно определить функцию, которая будет обращаться к наследованным функциям-членам класса TBase:
void ChangeCount(int n) {SetCount(GetCount() +n);}
Защищенные члены класса — это нечто среднее между от-крытыми и закрытыми членами. Подобно закрытым они доступны только функциям-членам класса. Вне класса они недоступны. Подобно открытым они доступны функциям-членам производных классов.
— закрытые члены доступны только в том классе, в кото-ром они объявлены;
— защищенные члены доступны членам их собственного класса и всем членам производных классов;
— открытые члены доступны членам их собственного класса, членам производных классов и всем остальным пользователям этого класса.
Спецификаторы (public, private, protected) влияют на статус наследуемых членов:
— члены открытого базового класса в производном классе сохраняют свои спецификации доступа;
— открытые члены защищенного базового класса становятся закрытыми в производном классе. Защищенные и закрытые сохраняют свои первоначальные спецификации;
— все члены закрытого базового класса становятся закрытыми в производном классе независимо от их перво-начальной спецификации доступа.
Если хотите сделать открытыми в производном классе только некоторые из наследуемых членов, можно выбо-рочно квалифицировать один или несколько из них.
class A {
public:
int x;
A() { x=0;}
void Display(void) { count << «x = » << x << endl;}
};
class B: private A {
public:
A::Display; // квалифицируем как открытый член класса
B(): A() {}
};
В производном классе деструктор нужен только в том случае, если необходимо что-то удалять.
// Программа 1
#include <iostream.h>
#include <string.h>
class TBase { //Базовый класс
private:
char *basep;
public:
TBase(const char *s) {basep = strdup(s); }
~TBase() {delete basep;}
const char *GetStr(void) { return basep; }
};
class TDerived: public TBase { //Производный класс
private:
char *uppercasep;
public:
TDerived(const char* s): TBase(s) { uppercasep = strupr(strdup(s));}
~TDerived() {delete uppercasep;}
const char *GetUStr(void) { return uppercasep;}
};
void main()
{
TBase president(«George Washington»);
cout << «Original string: » << president.GetStr() << endl;
TDerived pres(«George Washington»);
cout << «Uppercase string:» << pres.GetUStr() << endl;
}
В производном классе обычно добавляются новые дан-ные и функции-члены. Однако существует возможность заме-щения функций-членов, наследованных от базового класса. Например:
//Программа 2
#include <iostream.h>
#include <string.h>
class TBase { //Базовый класс
private:
char *basep;
public:
TBase(const char *s) {basep = strdup(s); }
~TBase() {delete basep;}
void Display(void) { cout << basep << endl;}
};
class TState: public TBase { //Производный класс
public:
TState(const char *s):TBase (s) {}
void Display(void); // замещающая функция
};
void TState::Display(void)
{
cout << «State: «; //Новый оператор
TBase::Display(); //Вызов замещенной функции
}
void main()
{
TState object(«Welcome to Borland C++5 programming!»);
object.Display();
}
3.2. Множественное наследование
Построение производного класса на основе нескольких базовых выглядит очень просто: вместо имени одного базового класса (вместе с его атрибутом) используется список имен, разделенный запятыми, например:
class A { /*…*/ };
class B { /*…*/ };
class C: public A, private B { /*…*/ };
Передача аргументов конструкторам базовых классов из конструктора производного класса производится так же как и раньше:
С::С( int a, char* str) : A(a), B(str) { /*…*/ };
3.3. Виртуальные функции
Виртуальная функция — это функция, вызов которой за-висит от типа объекта. В ООП можно писать виртуальные функции так, чтобы объект определял, какую функцию необ-ходимо вызвать, во время выполнения программы. Технику ис-пользования виртуальных функций иногда называют полимор-физмом, намекая на то, что объекты имеющие дело с вирту-альными функциями кажутся приспосабливающимися к контек-сту, в котором они используются.
Для лучшего понимания виртуальных функций ознакомьтесь с важным принципом классов С++, cвязанных отношением насле-дования. Cогласно правилам С++, указатель на базовый класс может ссылаться на объект этого класса или на объект любо-го производного от базового.
Пример: имеется 3 класса: класс В производный от класса А, а класс С производный от класса В.
A aObject; //Объявление объектов классов
B bОbject;
C cОbject;
По определению указатель на класс А может ссылаться на любой из этих объектов, так как они связаны наследова-нием. Эта взаимосвязь работает только в одном направлении.
A *p;
p = &cОbject;
Этот принцип становится особенно важным, если в свя-занных отношениями родства классах определяются вирту-альные функции.
Эти Функции имеют точно такой же вид и программируются точно так же, как обыкновенные, но добавляется ключевое слово virtual.
class A
{ public:
virtual void vf();
};
Виртуальная функция может объявляться с параметрами, она может возвращать значение, как и любая другая функция.
class B: public A {
public:
virtual void vf();
};
Когда в классе определяется виртуальная функция, имеющая одинаковое имя с виртуальной функцией класса-предка, такая функция называется замещающей. Указатель *р ссылается на объект типа С, следовательно
р->vf(); //указывает на виртуальную функцию класса С.
// Программа 3 //Пример виртуальной функции
#include <iostream.h>
class TValue {
public:
virtual double func(double x) { return x*x; }
double culc(double x) { return func(x)/2; }
};
class DValue: public TValue {
public:
virtual double func(double x) { return x*x*x; }
};
void main()
{
TValue obj1;
cout << obj1.culc(3) << endl;
DValue obj2;
cout << obj2.culc(3) << endl;
}
Чистая виртуальная функция — это пустое место, ожи-дающее своего заполнения производным классом. Объявляется точно так же, как и обычная, только ее объявление в классе завершается = 0.
Например:
class Abstract {
public:
virtual void f1(void) ; //обычная виртуальная
virtual void f2(void) = 0 ; //чистая виртуальная
};
Компилятору не потребуется реализация функции-члена f2 в отличии от остальных функций-членов в классе.
Если класс содержит хотя бы одну чисто виртуальную функцию-член, он называется абстрактным классом. Абстракт-ный класс это всего лишь схема, на основании которой можно создавать производные классы. Чтобы использовать абстракт-ный класс, следует вывести из него новый класс:
class MyClass: public Abstract {
public:
virtual void f2(void); //бывшая чисто виртуальная функция
};
