вернуться в оглавление предыдущая глава предыдущий параграф следующий параграф следующая глава


5.1. Типы наследования. Видимость членов классов

5.1.1. Наследование

Наследование - такое отношение между классами, когда один из них наследует (повторяет) структуру и поведение другого. Содержание (переменные-члены) и поведение (функции-члены) наследуются и становятся членами другого класса (наследника).

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

// класс Линза
class Lens
{
    Position m_p;      // положение линзы в пространстве
    double m_D;        // диаметр линзы
    Media m_media;     // оптическая среда линзы
    double m_R1, m_R2; // радиусы кривизны линзы
    double m_d;        // толщина линзы (осевое расстояние) 
};
// класс Зеркало
class Mirror
{
    Position m_p; // положение зеркала в пространстве
    double m_D;   // диаметр зеркала
    double m_R;   // радиус кривизны зеркала
};

Имеет смысл воспользоваться принципом наследования и реализовать, базовый класс Detail.

Создание базовых классов и классов-наследников в языке С++ осуществляется следующим образом.

class Detail
{
protected:
    Position m_p;  // положение детали в пространстве
    double m_D;    // диаметр детали
}; 
// класс-наследник Линза
class Lens : public Detail
{
    Media m_media;     // оптическая среда линзы
    double m_R1, m_R2; // радиусы кривизны линзы
    double m_d;        // толщина линзы (осевое расстояние) 
};
// класс-наследник Зеркало
class Mirror : public Detail
{
    double m_R;   // радиус кривизны зеркала
};

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

Таким образом, и Lens и Mirror являются деталями, а параметры детали наследуются, и в результате также являются членами этих классов.

5.1.2. Пример 5.1. Линза и зеркало как оптические детали

/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 5.1. Класс Деталь
// Класс Деталь. detal.h
// 
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
// проверка на повторное подключение файла
#if !defined DETAIL_H
#define DETAIL_H 

/////////////////////////////////////////////////////////////////////////////
// класс Деталь - базовый класс для всех оптических деталей
class Detail
{
protected:
    // координата детали по оси z
    double m_z; 
    // диаметр детали
    double m_D;

public:
    // конструкторы и деструктор
    Detail();
    Detail(double z, double D);
    ~Detail();

    // установить диаметр детали
    void Set_D(double D); 
    // получить диаметр детали
    double Get_D() const; 
    // установить координату по оси z
    void Set_z(double z); 
    // получить координату по оси z
    double Get_z() const; 

    // печать параметров детали
    void print() const;	
}; 
/////////////////////////////////////////////////////////////////////////////
// установить показатель преломления 
inline void Detail::Set_D(double D)
{
    m_D=D; 
} 
/////////////////////////////////////////////////////////////////////////////
// получить показатель преломления 
inline double Detail::Get_D() const
{
    return m_D; 
} 
/////////////////////////////////////////////////////////////////////////////
// установить координату по оси z
inline void Detail::Set_z(double z)
{
    m_z=z; 
} 
/////////////////////////////////////////////////////////////////////////////
// получить координату по оси z
inline double Detail::Get_z() const
{
    return m_z; 
} 
/////////////////////////////////////////////////////////////////////////////
#endif //defined DETAIL_H

 

/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 5.1. Линза и зеркало как оптические детали
// Класс Деталь. detal.cpp
// 
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
#include <iostream>
using namespace std;

#include "detail.h"
/////////////////////////////////////////////////////////////////////////////
// конструктор 
Detail::Detail()
: m_z(0)
, m_D(0)
{
    cout<<"constructor Detail()"<<endl;
}
/////////////////////////////////////////////////////////////////////////////
// конструктор 
Detail::Detail(double z, double D)
: m_z(z)
, m_D(D)
{
    cout<<"constructor Detail(z,D)"<<endl;
}
/////////////////////////////////////////////////////////////////////////////
// деструктор
Detail::~Detail()
{
    cout<<"destructor Detail"<<endl;
}
/////////////////////////////////////////////////////////////////////////////
// печать параметров
void Detail::print() const
{
    cout<<"Detail: "<<m_z<<" "<<m_D<<"    ";
}
/////////////////////////////////////////////////////////////////////////////

 

/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 5.1. Линза и зеркало как оптические детали
// Класс Линза. lens.h
// 
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
// проверка на повторное подключение файла
#if !defined LENS_H
#define LENS_H 

#include "detail.h"
/////////////////////////////////////////////////////////////////////////////
// класс Линза - наследник от класса Деталь
class Lens : public Detail
{
protected:
    // радиусы кривизны линзы
    double m_r1, m_r2;
    // осевое расстояние
    double m_d;
    // показатель преломления
    double m_n;

public:
    // конструкторы и деструктор
    Lens();
    Lens(double r1, double r2, double d, double n, double z, double D);
    ~Lens();

    // установка показателя преломления 
    void Set_n(double n); 
    // получение показателя преломления 
    double Get_n() const; 
    // установка осевого расстояния 
    void Set_d(double d); 
    // получение осевого расстояния
    double Get_d() const; 
    // установка 1го радиуса 
    void Set_r1(double r); 
    // получение 1го радиуса
    double Get_r1() const; 
    // установка 2го радиуса 
    void Set_r2(double r); 
    // получение 2го радиуса
    double Get_r2() const; 

    // печать параметров линзы
    void print() const;
    // вычисление хода луча через линзу
    void RayTrace();
}; 
/////////////////////////////////////////////////////////////////////////////
// установка показателя преломления 
inline void Lens::Set_n(double n)
{
    m_n=n; 
} 
/////////////////////////////////////////////////////////////////////////////
// получение показателя преломления
inline double Lens::Get_n() const
{
    return m_n; 
} 
/////////////////////////////////////////////////////////////////////////////
// установка осевого расстояния
inline void Lens::Set_d(double d)
{
    m_d=d; 
} 
/////////////////////////////////////////////////////////////////////////////
// получение осевого расстояния
inline double Lens::Get_d() const
{
    return m_d; 
} 
/////////////////////////////////////////////////////////////////////////////
// установка 1го радиуса
inline void Lens::Set_r1(double r)
{
    m_r1=r; 
} 
/////////////////////////////////////////////////////////////////////////////
// получение 1го радиуса
inline double Lens::Get_r1() const
{
    return m_r1; 
} 
/////////////////////////////////////////////////////////////////////////////
// установка 2го радиуса
inline void Lens::Set_r2(double r)
{
    m_r2=r; 
} 
/////////////////////////////////////////////////////////////////////////////
// получение 2го радиуса
inline double Lens::Get_r2() const
{
    return m_r2; 
} 
/////////////////////////////////////////////////////////////////////////////
#endif //defined LENS_H

 

/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 5.1. Линза и зеркало как оптические детали
// Класс Линза. lens.cpp
// 
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
#include <iostream>
using namespace std;

#include "lens.h"
/////////////////////////////////////////////////////////////////////////////
// конструктор 
Lens::Lens()
: m_n(1)
, m_r1(0)
, m_r2(0)
, m_d(0)
{
    cout<<"constructor Lens()"<<endl;
}
/////////////////////////////////////////////////////////////////////////////
// конструктор 
Lens::Lens(double r1, double r2, double d, double n, double z, double D)
: Detail(z, D)
, m_n(n)
, m_r1(r1)
, m_r2(r2)
, m_d(d) 
{

    cout<<"constructor Lens(r1,r2,d,n)"<<endl;
}
/////////////////////////////////////////////////////////////////////////////
// деструктор
Lens::~Lens()
{
    cout<<"destructor Lens"<<endl;
}
/////////////////////////////////////////////////////////////////////////////
// печать параметров
void Lens::print() const 
{
    Detail::print();
    cout<<"Lens: "<<m_n<<" "<<m_r1<<" "<<m_r2<<" "<<m_d<<endl;
} 
/////////////////////////////////////////////////////////////////////////////
// вычисление хода луча
void Lens::RayTrace()
{
    cout<<"Lens: RayTrace"<<endl;
}
/////////////////////////////////////////////////////////////////////////////

 

/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 5.1. Линза и зеркало как оптические детали
// Класс Зеркало. mirror.h
// 
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
// проверка на повторное подключение файла
#if !defined MIRROR_H
#define MIRROR_H 

#include "detail.h"
/////////////////////////////////////////////////////////////////////////////
// класс Зеркало - наследник от класса Деталь
class Mirror : public Detail
{
private:
    // радиус кривизны зеркала
    double m_r;

public:
    // конструкторы и деструктор
    Mirror();
    Mirror(double r, double z, double D);
    ~Mirror();

    // установка радиуса 
    void Set_r(double r); 
    // получение радиуса
    double Get_r() const; 

    // печать параметров зеркала
    void print() const;	
    // вычисление хода луча через зеркало
    void RayTrace();
};
/////////////////////////////////////////////////////////////////////////////
// установка радиуса
inline void Mirror::Set_r(double r)
{
    m_r=r; 
} 
/////////////////////////////////////////////////////////////////////////////
// получение радиуса
inline double Mirror::Get_r() const
{
    return m_r; 
} 
/////////////////////////////////////////////////////////////////////////////
#endif //defined MIRROR_H

 

/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 5.1. Линза и зеркало как оптические детали
// Класс Зеркало. mirror.cpp
// 
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
#include <iostream>
using namespace std;

#include "mirror.h"
/////////////////////////////////////////////////////////////////////////////
// конструктор 
Mirror::Mirror()
: m_r(0)
{
    cout<<"constructor Mirror()"<<endl;
}
/////////////////////////////////////////////////////////////////////////////
// конструктор 
Mirror::Mirror(double r, double z, double D)
: Detail(z, D)
, m_r(r)
{
    cout<<"constructor Mirror(r)"<<endl;
}
/////////////////////////////////////////////////////////////////////////////
// деструктор
Mirror::~Mirror()
{
    cout<<"destructor Mirror"<<endl;
}
/////////////////////////////////////////////////////////////////////////////
// печать параметров
void Mirror::print() const 
{
    Detail::print();
    cout<<"Mirror: "<<m_r<<endl;
} 
/////////////////////////////////////////////////////////////////////////////
// вычисление хода луча
void Mirror::RayTrace()
{
    cout<<"Mirror: RayTrace"<<endl;
}
/////////////////////////////////////////////////////////////////////////////

 

/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 5.1. Линза и зеркало как оптические детали
// main.cpp
// 
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
#include <iostream>
using namespace std;

#include "lens.h"
#include "mirror.h"
/////////////////////////////////////////////////////////////////////////////
void main()
{
    // линза
    cout<<"test lens:"<<endl;
    Lens l(100, -100, 10, 1.5, 0, 20);
    l.print();

    // зеркало
    cout<<endl<<"test mirror:"<<endl;
    Mirror m(100, 50, 25);
    m.print();

    cout<<endl;
}
/////////////////////////////////////////////////////////////////////////////

5.1.3. Последовательность вызова конструкторов

При создании экземпляра класса Lens будет вызван сначала конструктор базового класса (Detail()) и только после этого конструктор класса Lens(). При разрушении экземпляра класса Lens деструкторы будут вызваны в обратном порядке: сначала - деструктор ~Lens(), а затем - ~Detail().

В полном конструкторе (конструкторе копии) наследника обязательно нужно вызывать соответствующий конструктор (полный или копии) базового класса. Если этого не сделать, то компилятор вызовет конструктор по умолчанию для базового класса. Если и его нет, то компилятор выдаст сообщение об ошибке.

5.1.4. Типы наследования. Видимость членов классов

Возможно наследование нескольких типов – public, protected и private:

  • class X : public Y В этом случае наследник является подтипом и должен выполнять все обязательства родителя. Допустимо преобразование X* в Y*
  • class X : private Y Наследуя структуру и поведение родителя, наследник не будет его подтипом. public и protected члены родителя станут private членами наследника. Преобразование X* в Y* допустимо для друзей и членов X . Последующее наследование не имеет смысла.
  • class X : protected Y Наследуя структуру и поведение, наследник не будет подтипом. public и protected члены родителя станут protected членами наследника. Преобразование X* в Y* допустимо для членов и друзей и наследников X

При этом доступ к переменным-членам базового класса будет определяться не только типом видимости членов класса (раздел 4.2.3), но и типом наследования. В таблице показано какой тип доступа будет к членам базового класса, в зависимости от их типа и типа наследования.

 

public (открытые)

protected (защищенные)

private (закрытые)

public-наследование

public

protected

недоступны

protected-наследование

protected

protected

недоступны

private-наследование

private

private

недоступны

5.1.5. Множественное наследование

Множественное наследование – это наследование свойств одновременно от двух базовых классов. Например, класс Сферическое зеркало может одновременно наследовать свойства от классов Сферическая поверхность и Зеркало.

Если класс Child наследуется от двух базовых классов ( Base1 и Base2), то в классе Child будет возможность использования параметров из обоих базовых классов. При создании экземпляра класса Child конструкторы будут вызваны в следующей последовательности: Base1(), Base2() и только после этого конструктор класса Child().

При разрушении экземпляра класса Child деструкторы будут вызваны в обратном порядке: сначала - деструктор ~Child(), затем - ~Base2(), а затем ~Base1().

class Base1
{
protected:
    double m_param1;

public:
    Base1();
    ~Base1();
};
class Base2
{
protected:
    double m_param2;

public:
    Base1();
    ~Base1();
};
class Child : public Base1, public Base2
{
public:
    Child();
    ~Child();
};