12 虚函数与多态

时间:2023-01-28 21:53:59

1. 基类的指针

因当创建一个类的对象时会创建它的父类对象.如下代码:

class Animal {

public:

int num;

};


class Human : public Animal {

public:

int ret;

};


class MM : public Human {


};


Animal *a = new MM; //基类的指针指向派生类对象

Human *h = new MM; //父类的指针指向子类对象.


因父类/基类是比子类/派生类的空间小,所以基类的指针可以指向它的派生类的对象.

派生类的指针不可以指向父类/基类的对象,因派生类指针可访问的范围大



2. 通过父类指针回收子类对象空间的问题.


class Animal {

public:

Animal() {

cout << "animal init" << endl;

}

~Animal() {

cout << "animal exit" << endl;

}

};


class Human : public Animal {

private:

int *data;

public:

Human() {

data = new int[1024];

cout << "human init " << endl;

}

~Human() {

delete [] data;

cout << "human exit " << endl;

}

};



int main(void)

{

Animal *a = new Human;

delete a;

return 0;

}


编译执行后的输出:

[root@localhost 12multi-stats]# ./a.out

animal init

human init

animal exit


发现通过父类指针回收子类对象时子类的析构函数没有得到触发.

解决这个问题,只要把基类的构造函数声明为virtual函数.通常情况下,基类的析构函数都是声明为虚函数的.



3. 函数重写

如下面,父类子类都实现了一个eat函数,当通过子类对象来调用函数时,使用子类里的函数。但父类的函数也是存放的,也可以指定调用父类里的函数.

using namespace std;


class Animal {

public:

void eat() {

cout << "animal eat" << endl;

}

};


class Human : public Animal {

public:

void eat() {

Animal::eat(); //指定调用父类里的函数

cout << "human eat" << endl;

}

};


int main(void)

{

Human h;

h.eat();

h.Animal::eat(); //指定调用父类里的eat函数


return 0;

}


4. 虚函数的作用

class Animal {

public:

void eat() {

cout << "animal eat" << endl;

}

};


class Human : public Animal {

public:

void eat() {

cout << "human eat" << endl;

}

};


int main(void)

{

Animal *a = new Human;


a->eat();

return 0;

}


当代码执行时,会发现调用的其实是父类的eat函数,但从合理来说,父类指针指向的是子类的对象,应是调用子类重新实现的函数.要想调用到子类新实现的函数,只需把父类里的同名函数声明为virtual即可.


虚函数的作用就是通过基类指针提供一种方法,可以调用到派生类里新实现的函数功能。实现代码往后兼容的目的。也是可以实现先写好框架,这框架不管后面怎样的扩展,都可以不用改变的.


虚函数是函数体,纯虚函数其实就没有函数体的虚函数.用于指定派生类里必须要实现的函数.

纯虚函数:

class Animal {

public:

virtual void eat() = 0;

}



5. 多类其实就是使用虚函数实现的功能.


例如,写个游戏的副本:


class Role {

public:

virtual void attack() = 0;

virtual void jump() = 0;

void play() { //游戏副本:跳, 跳 , 杀, 跳

jump();

jump();

attack();

jump();

cout << "###################" << endl;

}

};


写好副本后,不管什么样的角色,只要继承了Role,实现虚函数,当调用play进入副本时都可以得到调用.

而且不管继承多少层,都是可以兼容的


class Magician : public Role {

public:

void attack() {

cout << "magician attack" << endl;

}


void jump() {

cout << "magician jump" << endl;

}

};


class Knight : public Role {

public:

void attack() {

cout << "knight attack" << endl;

}

void jump() {

cout << "kngiht jump" << endl;

}

};


class SuperKnight : public Knight {

public:

void attack() {

cout << "super knight attack" << endl;

}

void jump() {

cout << "super kngiht jump" << endl;

}

};


int main(void)

{

SuperKngiht sk;

sk.play();


Magician m;

m.play();


return 0;

}