Язык C++ позволяет классу наследовать элементы данных и элементыфункции одного или нескольких других классов. Другими словами, новый класс может получить атрибуты и поведение от уже существующего класса. Новый класс называют производным классом. Класс, элементы которого наследуются производным классом, называется базовым классом. В свою очередь, производный класс может служить базовым для другого класса.
Наследование дает возможность заключить, т.е. абстрагировать, некоторое общее или схожее поведение различных объектов в одном базовом классе. Несколько классов будут затем наследовать это поведение, являясь производными от базового.
Наследование позволяет также немного изменить поведение существующего класса. Производный класс может переопределить некоторые функции-элементы базового, наследуя тем не менее основной объем свойств и атрибутов базового класса.
Синтаксис наследования имеет следующий вид:
class Base
{ … };
class Derive : ключ_доступа Base [, ключ_доступа Base2, …]
{ … };
Ключ_доступа не является обязательным и может быть private, protected или public для структур.
Ключ доступа определяет правила доступа к элементам базового класса внутри производного. Рассмотрим возможные варианты доступа (см. табл. 6).
Таблица 6
Правила доступа к элементам базового класса
Если базовый класс наследуется как private, его public-элементы будут являться private в производном классе. Однако вы можете выборочно сделать некоторые из элементов базового класса доступными как public в производном классе, указав их в public секции производного класса. Например:
class Base
{
public:
void f1();
void f2();
};
class Derived : private Base
{
public:
Base::f1; /* делает void Base::f1() доступной как public */
};
Простым наследованием называется случай, когда производный класс имеет всего один базовый класс. Например:
#include <stdio.h>
#include <conio.h>
#include <string.h>
const int MAX_LEN = 10;
class CCoord
{
protected:
int iX, iY;
public:
CCoord(int _iX=0, int _iY=0);
void SetLoc(int _iX, int _iY);
};
CCoord::CCoord(int _iX, int _iY)
{SetLoc(_iX, _iY);};
CCoord::SetLoc(int _iX, int _iY)
{
iX = _iY;
iY = _iY;
}
class CMsgAtCoord: public CCoord
{
char msg[MAX_LEN];
public:
MsgAtCoord(char *_msg = “NO MSG”);
void SetMsg(char *_msg);
void Show();
};
CMsgAtCoord::CMsgAtCoord(char _msg = “NO MSG”)
{ SetMsg(_msg); };
void CMsgAtCoord::SetMsg(char *_msg)
{ strcpy(msg, _msg); };
CMsgAtCoord::Show()
{
gotoxy(iX, iY);
printf(msg);
}
int main(void)
{
CMsgAtCoord greeting;
greeting.SetLoc(10, 10);
greeting.SetMsg(“Hello…”);
greeting.Show();
}
Конструкторы не наследуются. Если конструктор базового класса требует спецификации одного или нескольких параметров, конструктор производного класса должен вызвать базовый конструктор, используя список инициализации элементов. Например:
#include <string.h>
class Base
{
public:
Base(int, float);
};
class Derived: public Base
{
public:
Derived(char *lst, float f);
};
Derived::Derived(char *lst, float f): Base (strlen(lst), f)
{}
Деструктору производного класса, напротив, не требуется явно вызывать деструктор базового класса. В деструкторе производного класса компилятор автоматически генерирует вызовы базовых деструкторов.
В C++ допускается множественное наследование, когда класс является производным от нескольких базовых классов. Это позволяет вам в одном производном классе сочетать поведение нескольких классов. Однако следует помнить, что множественное наследование может привести к ряду проблем.
Так, если два базовых класса содержат методы с одинаковыми именами, то компилятор не сможет вычислить в производном классе, какой из двух методов базовых классов выбрать. Проблема решается использованием оператора разрешения области видимости, явно указывающего на класс, в котором находится используемый метод. Например:
#include <iostream>
using namespace std;
class A
{
public:
void show() { cout << "Класс A\n"; }
};
class B
{
public:
void show() { cout << "Класс B\n"; }
};
class C: public A, public B
{};
int main()
{
C objC; // объект класса C
// objC.show(); /* ошибка - программа не скомпилиру-ется*/
objC.A::show(); /* обращаемся к методу show класса A */
objC.B::show(); /*обращаемся к методу show класса B */
return 0;
}
Следующая ошибка, которая может возникнуть при множественном наследовании – это когда мы создаем производный класс от двух базовых клас-сов, которые, в свою очередь, являются производными одного класса. Это создает дерево наследования в форме ромба, например:
#include <iostream>
using namespace std;
class A
{
public:
void func();
};
class B: public A
{};
class C: public A
{};
class D: public B, public C
{};
int main()
{
D oD;
oD.func(); /* неоднозначность: программа не скомпи-лируется */
return 0;
}
Здесь классы B и C являются производными от класса A, а класс D является производным классов B и C. Трудности начинаются, когда объект класса D пытается воспользоваться методами класса A, в нашем примере, методом func(). Однако классы B и C содержат в себе копии метода func(), унаследованного от класса A. Компилятор не сможет определить, какой из методов использовать, и выдаст сообщение об ошибке. |