350 likes | 505 Views
Наследование. Композиция. class Circle { Point m_center ; double m_radius ; public : double GetSquare () const ; };. - отношение «класс А содержит класс Б». Разрабатываем второй класс:. class ColoredCircle { Point m_center ; double m_radius ; Color m_color ; public :
E N D
Композиция • classCircle • { • Pointm_center; • doublem_radius; • public: • doubleGetSquare() const; • }; -отношение «класс А содержит класс Б»
Разрабатываем второй класс: • classColoredCircle • { • Pointm_center; • doublem_radius; • Colorm_color; • public: • doubleGetSquare() const; • voidSetColor(Colorcolor); • };
classColoredCircle • { • Pointm_center; • doublem_radius; • Colorm_color; • public: • doubleGetSquare() const; • voidSetColor(Colorcolor); • }; • classCircle • { • Pointm_center; • doublem_radius; • public: • doubleGetSquare() const; • };
Wrapper(паттерн, который здесь не подходит) • classColoredCircle • { • Circlem_circle; • Color m_color; • public: • double GetSquare() const • { • returnm_circle.GetSquare(); • } • };
Наследование (оптимальное решение) • classColoredCircle : publicCircle • { • Colorm_color; • public: • voidSetColor(Color color); • }; ColoredCircle cc; cc.SetColor(green); var square = cc.GetSquare();
Наследование - отношение «класс А является классом Б» (IS-A) Иерархия классов 1. Велосипед является (is a) транспортным средством? 2. Транспортное средство является велосипедом?
Наследование – создание новых классов из уже существующих, путем • заимствования их данных и методов. • Как правило, производный класс является частным случаем • базового класса. Transport Aircraft сlassимя_класса: спецификатор_доступаимя_базового_класса { };
1. Наследование– создание нового класса на основе существующего. • 2. Содержимое базовогокласса заимствуется производным классом. • 3. Наследование позволяет использовать уже написанный код базового класса: • - дополняя его новыми полями данных • - дополняя его новыми методами • - переопределяя существующие методы • Достоинства наследования: • Позволяет изменять поведение классов с закрытым кодом. • Уменьшение дублирования кода. • Упрощается модификация программы. • Структура программы становится более лаконичной. • Недостатки наследования: • Сильная связность (проблема «хрупких базовых классов»).
Композиция vs. Наследование Label ColoredLabel Point Circle Car Driver File MP3File Man Student Композиция – «содержит» Наследование – «является частным случаем» В иерархии 1:1 нужно лишь правильно сделать выбор между композицией и наследованием (прибегая ко второму лишь при необходимости).
Конструкторы и наследование Первым всегда вызывается конструктор базового класса! Варианты вызова: 1. Неявный (автоматический) вызов – вызывается конструктор по умолчанию базового класса. 2. Явный вызов – в конструкторе производного класса через название класса- предка(можно передать параметры).
Порядок вызова конструкторов: • Конструкторы базового класса (в порядке объявления при наследовании). • Конструкторы объектов-элементов производного класса (в порядке объявления в теле класса). • Конструктор производного класса
class Window • { • intwidth; • intheight; • public: • Window() : width(0), height(0) • { • } • }; • class Button : public Window • { • std::string text; • public: • Button(std::string text) • { • this->text = text; • } • };
Window* wnd = new Window(); Button* btn = new Button("OK");
Явный вызов конструктора базового класса class Text { std::string text; public: Text(std::string text) : text(text) { } }; classColoredText : public Text { Colorcolor; public: ColoredText(std::string text, Color color) : Text(text), color(color) { } };
Пример наследования с дополнением • classButton • { • intm_left; • intm_top; • intm_width; • intm_height; • std::stringm_caption; • public: • Button(); • intGetWidth() const; • }; • classPictureButton: publicButton • { • Image m_icon; • public: • PictureButton(); • };
Пример наследования с ограничением • class Matrix • { • protected: • float** m; • introws; • intcols; • public: • Matrix(int rows, int cols) • { • } • }; • classSquareMatrix: public Matrix • { • public: • SquareMatrix (intsize) : Matrix(size, size) • { • } • float CalcDet(); • };
Пример наследования: общий предок • classCar • { • std::stringm_model; • float m_velocity; • public: • voidAccelerate(floataccel); • }; • classAvia • { • std::stringm_model; • float m_velocity; • float m_altitude; • public: • voidVerticalAccelerate(floataccel); • };
classTransport • { • std::stringm_model; • floatm_velocity; • }; • classCar : publicTransport • { • public: • voidHandBrakeOn(); • }; • classAvia : publicTransport • { • floatm_altitude; • public: • voidVerticalAccelerate(floataccel); • };
Классификация наследования (по ТимотиБадду): • 1. Специализация (наследник является специализированной формой предка). • 2. Спецификация (дочерний класс реализует поведение, описанное в предке). • 3. Конструирование или Варьирование (наследник использует методы предка, • но не является его подтипом). • 4. Расширение (в потомок добавляют новые методы, расширяя поведение предка). • 5. Обобщение (потомок обобщает поведение предка). • 6. Ограничение (потомок ограничивает поведение предка).
Наследование и спецификаторы доступа • Открытые элементы класса доступны всем. • Закрытые элементы класса доступны только его методам и друзьям класса. • Защищенные элементы базового класса доступны методам и друзьям базового • класса, а также методам и друзьям производного класса. • Методы производного класса могут обращаться к открытым и защищенным • элементам базового класса.
Контракт Принцип замещения Лисков: производные классы полностью поддерживают контракт базового (вместо объекта базового класса везде можно подставить объект производного). Открытое наследование – наследование интерфейса. Закрытое наследование – наследование реализации.
Замещающая функция • class Man • { • std::string name; • public: • std::string ToString() • { • return name; • } • };
#include<sstream> • class Student : public Man • { • intgroup; • public: • std::string ToString() • { • std::stringstreamss; • ss<< Man::ToString() • << ", " • << group; • returnss.str(); • } • };
Множественное наследование • classIstream • { • std::string read(); • } • classOstream • { • void write(std::string str); • } • classIOstream : publicIstream, publicOstream • { • } Возможен конфликт имен
class Foo • { • public: • Foo(): a(b), b(c), c(0){} • private: • inta; • intb; • intc; • };
class A { public: A() {} }; class B { public: B() {} }; class C : public A, public B { public: C(): B(), A() {} };
Конфликт имен и его разрешение • class A • { • public: • std::string GetTypeName() { return "A"; } • }; • class B • { • public: • std::string GetTypeName() { return "B"; } • }; • class C: public A, public B • { • }; C c; std::cout << c.GetTypeName();//?
Виртуальное наследование class A { public: void f(){} }; class B : virtual public A { }; class C : virtual public A { }; class D : public B, public C { };
Приведения типов intwidth = 1024; intheight = 768; floatasp = float(width) / height; floatpi= 3.14; intp= static_cast<int>(pi);
float f = 1.0f; int* n = static_cast<int*>(&f); int* n = reinterpret_cast<int*>(&f); // Ошибка компиляции int i = 0; constint* pI = &i; int* j = const_cast<int*> (pI);
Понижающее приведение class Base { }; class Child : public Base { }; void main() { Base* pBase = new Child(); Child* pChild = static_cast<Child*>(pBase); }
class Alien { }; Base* pBase = (Base*)new Alien(); Child* pChild = static_cast<Child*>(pBase); //?
class Base • { • virtual void f(){} • }; • class Alien • { • virtual void ff(){} • }; • void main() • { • Base* pBase = (Base*)new Alien(); • Child* pChild = dynamic_cast<Child*>(pBase); • if (pChild){ • } • }