MyTetra Share
Делитесь знаниями!
Наследование и полиморфизм
Время создания: 19.03.2018 10:30
Раздел: Computer - Programming - C++

Предположим, мы создаем игру со следующими персонажами: Строитель, Доктор, Бабушка.


#include <stdio.h>

#include <iostream>

#include <string>


using namespace std;


class Builder

{

private:

string name;

unsigned int age;

unsigned int force;

public:

void information(void)

{ cout << "My name is " << name << ". I'm " << age << endl; }


void sleep(void)

{ force += 50; }


void build(void)

{ force -= 10;

cout << "Bam-Bam-Bam (" << force << ")" << endl; }


};


class Doctor

{

protected:

string name;

unsigned int age;

unsigned int force;

public:

void information(void)

{ cout << "My name is " << name << ". I'm " << age << endl; }


void sleep(void)

{ force += 50; }


void treat(void)

{ force -= 10;

cout << "I will help you, hold on! (" << force << ")" << endl; }


};


class Grandma

{

private:

string name;

unsigned int age;

unsigned int force;

public:

void information(void)

{ cout << "My name is " << name << ". I'm " << age << endl; }


void sleep(void)

{ force += 50; }


void feed(void)

{ force -= 10;

cout << "You are so thin! Eat more... (" << force << ")" << endl; }


};


int main()

{

Builder b;

b.information();


getchar();

return 0;

}


Реализованные, в виде классов, персонажи обладают общими типовыми параметрами (с разными значениями): имя, возраст, сила;

и методами: information(), sleep().


Данные повторения можно вынести в отдельный общий класс, например, Human:


class Human

{

protected:

string name;

unsigned int age;

unsigned int force;

public:

void information(void)

{ cout << "My name is " << name << ". I'm " << age << endl; }


void sleep(void)

{ force += 50; }

};


class Builder: public Human

{

public:

void build(void)

{ force -= 10;

cout << "Bam-Bam-Bam" << endl; }

};


class Doctor: public Human

{

public:

void treat(void)

{ force -= 10;

cout << "I will help you, hold on!" << endl; }

};


class Grandma: public Human

{

public:

void feed(void)

{ force -= 10;

cout << "You are so thin! Eat more..." << endl; }

};


Наследование может быть множественным. Например, Grandma наследник класса Doctor, который в свою очередь наследует класс Human.


class Grandma: public Doctor

{

public:

void feed(void)

{ force -= 10;

cout << "You are so thin! Eat more..." << endl; }

};


int main()

{

Grandma g;

g.information();

g.treat();

g.feed();


getchar();

return 0;

}


Так же наследники могут перегружать родительские методы. Пример с information():


class Grandma: public Doctor

{

public:

void information(void)

{ cout << "Once upon time..." << endl; }


void cook(void)

{

force -= 10;

cout << "You are so thin! Eat more..." << endl;

}

};


Вызов


Grandma g;

g.information();


Выведет на экран "Once upon time..." вместо стандартного "My name is ".


Далее другой пример:


class A

{

public:

void print()

{ cout << "A" << endl; }

};


class B: public A

{

public:

void print()

{ cout << "B" << endl; }

};


int main()

{

A t1;

B t2;


t1.print();

t2.print();


getchar();

return 0;

}


Выведет на экран


A

B


Как и ожидалось, метод print() в классе В перегружается. Это применимо как для статического, так и для динамического выделения памяти:


int main()

{

A *p1 = new A;

B *p2 = new B;


p1->print();

p2->print();


getchar();

return 0;

}


Вывод будет одинаковым.

Более того, вывод не поменяется, если написать следующим образом:


int main()

{

A *p1 = new B;

B *p2 = new B;


p1->print();

p2->print();


getchar();

return 0;

}


У нас указатель класса А p1 указывается на объект класса В. В данном случае это не вызывает ошибки, т.к. указатели родительского типа могут указывать на объекты-наследники.

Это называется полиморфизмом.

Т.е. строка A *p1 = new B; или A *p1 = new С; не вызовет ошибки, но указатель будет обращаться к родительскому методу print(), выводящему А.

Чтобы вызывался метод, соответствующий классу, на который указывает указатель, то метод родительского класса надо объявить как virtual


class A

{

public:

virtual void print()

{ cout << "A" << endl; }

};


class B: public A

{

public:

void print()

{ cout << "B" << endl; }

};


int main()

{

A *p1 = new B;

B *p2 = new B;


p1->print();

p2->print();


getchar();

return 0;

}


В таком случае вывод будет


В

В


Если при наследвоании указать тип, отличный от public, то все параметры родительского класса будут недоступны извне (private), либо доступны только наследникам (protected).


Возвращаясь к пример с персонажами, вызов:


int main()

{

Human *p1 = new Grandma;

Human *p2 = new Doctor;


p1-> information();

p2-> information();


getchar();

return 0;

}


Выведет одинаковое приветствие, не смотря на то, что в классе Grandma метод information() перегружен.

Чтобы использовался метод класса-наследника, необходимо родительский метод объявить как virtual


public:

virtual void information(void)

{ cout << "My name is " << name << ". I'm " << age << endl; }


В родительском классе можно и вовсе определить метод, равным 0:


class Human

{

protected:

string name;

int age;

int force;

public:

virtual void information(void) = 0;


void sleep(void)

{ force += 50; }

};


Это говорит о том, что все наследники должны объявить этот метод для себя, и он будет уникальным.


class Builder: public Human

{

public:

void information(void)

{ cout << "Ready to work!" << endl; }

...

};


class Doctor: public Human

{

public:

void information(void)

{ cout << "Medic is here!" << endl; }

...

};


class Grandma: public Doctor

{

public:

void information(void)

{ cout << "Once upon time..." << endl; }

...

};


Допустим, мы хотим создать множество переменных и вызвать их методы:


int main()

{

Builder t1;

Doctor t2;

Grandma t3;

Builder t4;


t1.information();

t2.information();

t3.information();

t4.information();


getchar();

return 0;

}


Но такой вызов неудобен, если объектов создано большое количество. Удобней создавать объекты динамически и укладывать их в массив:


int main()

{

Human *mass[4];

mass[0] = new Builder;

mass[1] = new Doctor;

mass[2] = new Grandma;

mass[3] = new Builder;


for(int i = 0; i < 4; ++i)

{

mass[i]->information();

}


delete mass[0];

delete mass[1];

delete mass[2];

delete mass[3];


getchar();

return 0;

}


Мы создаем массив из указателей родительского класса, затем динамически создаем объекты и привязываем к ним указатели.

Если так сделать возможно, то это показатель наличия полиморфизма в языке.











//************************


#include <stdio.h>

#include <iostream>

#include <string>


using namespace std;

/*using std::cout;

using std::cin;

using std::endl;

*/


class Human

{

protected:

string name;

int age;

int force;

public:

virtual void information(void) = 0;

// { cout << "My name is " << name << ". I'm " << age << endl; }


void sleep(void)

{ force += 50; }

};


class Builder: public Human

{

public:

void information(void)

{ cout << "Ready to work!" << endl; }


void build(void)

{

force -= 10;

cout << "Bam-Bam-Bam" << endl;

}

};


class Doctor: public Human

{

public:

void information(void)

{ cout << "Medic is here!" << endl; }


void treat(void)

{

force -= 10;

cout << "I will help you, hold on!" << endl;

}

};


class Grandma: public Doctor

{

public:

void information(void)

{ cout << "Once upon time..." << endl; }


void cook(void)

{

force -= 10;

cout << "You are so thin! Eat more..." << endl;

}

};


/*class A

{

public:

int a;

virtual void print()

{ cout << "A" << endl; }

};


class B: public A

{

public:

void print()

{ cout << "B" << endl; }

};*/


int main()

{

Human *mass[4];

mass[0] = new Builder;

mass[1] = new Doctor;

mass[2] = new Grandma;

mass[3] = new Builder;


for(int i = 0; i < 4; ++i)

{

mass[i]->information();

}


delete mass[0];

delete mass[1];

delete mass[2];

delete mass[3];


getchar();

return 0;

}











 
MyTetra Share v.0.53
Яндекс индекс цитирования