НГТУ, каф. ВТ Наследование в С++ Макаревич Л. Г.
Зачем нужно наследование Повторное использование кода – использование класса для создания объектов и в качестве базового для создания нового класса Построение иерархии классов позволяет выполнить упорядочивание классов При наследовании можно расширить характеристики класса, сузить их, изменить или уничтожить
Доступ при наследовании #include "stdafx.h" #include using namespace std; class A {int a; public: void f(){a++;} }; class B {int b; }; class C {int c; }; class D: public A {int d; public: void f(){D::f(); d++;} void q(){d = d + 55;} }; int main(int argc, char* argv[]) { A a1; D d1; cout
Простое наследование Не наследуются: Конструкторы Деструкторы Переопределенные операции new Переопределенные операции присваивания Отношения дружественности
Простое наследование Класс имеет один базовый класс class First {int a; public: void setA(int _a){a = _a;} }; class Second:public First {int b; public: void setB(int _b){b = _b;} }; class Third:public Second {int c; public: void setC(int _c){c = _c;} }; int main(int argc, char* argv[]) { First f; Second s; Third t; cout
Порядок вызова конструкторов – от базового класса (First, Second, Third) class First {int a; public: First(int _a=0):a(_a){} void setA(int _a){a = _a;} }; class Second:public First {int b; public: Second(int _a=0):First(_a){b = _b;} void setB(int _b){b = _b;} }; class Third:public Second {int c; public: Third(int _a=0):Second(_a){c = _c;} void setC(int _c){c = _c;} }; int main(int argc, char* argv[]) { First f; Second s; Third t; return 0; } class First {int a; public: First(int _a=0):a(_a){} void setA(int _a){a = _a;} }; class Second:public First {int b; public: Second(int _a=0){b = _b;} void setB(int _b){b = _b;} }; class Third:public Second {int c; public: Third(int _a=0){c = _c;} void setC(int _c){c = _c;} }; int main(int argc, char* argv[]) { First f; Second s; Third t; return 0; }
Порядок вызова деструкторов – в обратном порядке ~Third ~Second ~First class First {int a; public: First(int _a=0):a(_a){cout
Правила наследования различных методов Конструкторы не наследуются, поэтому производный класс должен иметь собственные конструкторы Если в конструкторе производного класса явный вызов конструктора базового класса отсутствует, то вызывается конструктор базового класса по умолчанию При многоуровневой иерархии вызов конструкторов начинается с самого верхнего уровня Если класс содержит члены другого класса, то сначала вызываются конструкторы членов класса, а потом конструктор класса Не наследуется операция присваивания. Ее надо явно определить в производном классе. Деструкторы не наследуются. Если деструктор в производном классе не определен, то он формируется по умолчанию и вызывает деструкторы базовых классов, причем в порядке, обратном вызову конструкторов.
Множественное наследование Table float height Table(float h) float Height() Circle float radius Circle(float h) float Area() RoundTable int color Radius(float h, float r, int col) int Color() Самостоятельно описать классы RoundTable t(90,75,5); cout
Вызов конструкторов базовых классов class RoundTable:public Table, public Circle { RoundTable(float h, float r, int c):Table(h),Circle(r),color(c){} }; Как изменится порядок вызова конструкторов, если запись будет иметь вид: class RoundTable:public Table, public Circle { RoundTable(float h, float r, int c):Circle(r), Table(h), color(c){} };
class A {int i, j; public: A(){i = j = 0;} A(int _i, int _j){i = _i; j = _j;} }; class B:public A {int i, j; public: B(){i = j = 0;} B(int _i, int _j){i = _i; j = _j;} }; class C:public A {int i, j; public: C(){i = j = 0;} C(int _i, int _j){i = _i; j = _j;} }; class D:public B, public C { public: D(int k):B(k,k),C(k,k){} } Виртуальные базовые классы AA BC D class A {int i, j; public: A(){i = j = 0;} A(int _i, int _j){i = _i; j = _j;} }; class B:public virtual A {int i, j; public: B(){i = j = 0;} B(int _i, int _j){i = _i; j = _j;} }; class C:public virtual A {int i, j; public: C(){i = j = 0;} C(int _i, int _j){i = _i; j = _j;} }; class D:public B, public C { public: D(int k):B(k,k),C(k,k){} } A BC D
Порядок вызова деструкторов при использовании виртуальных базовых классов Вначале вызывается конструктор виртуального базового класса, а затем конструкторы других классов в порядке объявления при наследовании Вызов деструкторов – в обратном порядке
class A {int i, j; public:A(){i = j = 0;} A(int _i, int _j){i = _i; j = _j;} }; class B:public A {int i, j; public:B(){i = j = 0;} B(int _i, int _j){i = _i; j = _j;} }; class C:public A {int i, j; public:C(){i = j = 0;} C(int _i, int _j){i = _i; j = _j;} }; class D:public B, public C { public:D(int k):B(k,k),C(k,k){} }; int main(int argc, char* argv[]) { A a; B b; C c; D d(55); cout
Разрешение видимости class A { public: int i, j; A(){i = j = 0;} A(int _i, int _j){i = _i; j = _j;} }; class B:public virtual A { public: int i, j; B(){i = j = 0;} B(int _i, int _j){i = _i; j = _j;} }; class C:public virtual A { public: int i, j; C(){i = j = 0;} C(int _i, int _j){i = _i; j = _j;} }; class D:public B, public C { public: D(int k):B(k,k),C(k,k){} } D d(55); //d.i d.A::i = 77; d.B::i = 77; d.C::i = 77;
Преобразование типов в наследовании Преобразование к базовому типу делается по умолчанию class A { int a; public: A(int _a=0){a=_a;;} }; class B:public A { int b; public: B(int _b=0):A(_b){b=_b;} }; int main(int argc, char* argv[]) { A * pa = new B(55); B * pb = (B*)pa; return 0; }
Переопределение функций при наследовании class A { int a; public: A(int _a=0){a=_a;;} int foo(){return 1;} }; class B:public A { int b; public: B(int _b=0):A(_b){b=_b;} int foo(){return 2;} }; int main(int argc, char* argv[]) { A * pa = new B(55); B * pb = (B*)pa; int i = pa->foo();//1 int j = pb->foo();//2 return 0; }
Виртуальные методы Полиморфизм class A { int a; public: A(int _a=0){a=_a;;} virtual int foo(){return 1;} }; class B:public A { int b; public: B(int _b=0):A(_b){b=_b;} int foo(){return 2;} }; int main(int argc, char* argv[]) { A * pa = new B(55); B * pb = (B*)pa; int i = pa->foo();//2 int j = pb->foo();//2 return 0; }
#include "stdafx.h" #include using namespace std; class Circle { protected: int rad; int x, y; public: Circle(int _rad, int _x, int _y ):rad(_rad),x(_x),y(_y){} void draw(){cout
Абстрактные классы Чистые виртуальные функции class Figure { int x, y; public: Figure(int _x=0, int _y = 0):x(_x),y(_y){} virtual void draw()=0; virtual ~Figure(){cout
Невиртуальные деструкторы Абстрактные классы. Чистые виртуальные функции class Figure { int x, y; public: Figure(int _x=0, int _y = 0):x(_x),y(_y){} virtual void draw()=0; ~Figure(){cout
Осторожность class Figure {int x, y; public: Figure(int _x=0, int _y = 0):x(_x),y(_y){} virtual void draw()=0; virtual ~Figure(){hide(); cout
int main(int argc, char* argv[]) { Figure ** p; *p = new (Figure)[3];//ошибка – абстрактный класс p[0] = new Circle (55); p[1] = new Rectangle (66,99); p[2] = new Circle(77); for ( int i = 0; i < 3; i++ ) p[i]->draw(); for ( i = 0; i < 3; i++ ) delete p[i]; return 0; } int main(int argc, char* argv[]) { Circle** p; *p = new (Circle)[3];// нет соответствующего конструктора p[0] = new Circle (55); p[1] = new Rectangle (66,99); p[2] = new Circle(77); for ( int i = 0; i < 3; i++ ) p[i]->draw(); for ( i = 0; i < 3; i++ ) delete p[i]; return 0; }
Вопросы Могут ли быть виртуальными функции-операторы? Могут ли быть виртуальными глобальные функции? Обязательно ли нужно переопределять виртуальную функцию? Можно ли вызвать виртуальную функцию базового класса? Можно ли вызывать виртуальную функцию в конструкторе?
Нельзя: Делать виртуальными конструкторы Делать виртуальными статические функции-члены класса
class A { public: A(){f();} virtual void f(){} }; class B:public A { public: B(){/* f(); */ } virtual void f(){} }; B * b = new B;// какая f() вызовется?
Различия структур, объединений и классов Доступ к элементам структур public Наследование в структурах с доступом public Объединения не наследуются Доступ к элементам объединений нельзя задавать, он public Элементы объединений не могут быть объекты классов