第十一站:C++面向对象-多态

发布时间:2024年01月20日

为什么要使用多态

当定义的子类继承父类并重写父类的方法后,

父类使用指针调用子类的同名方法,得到的却是父类同名方法的结果?

#include <iostream>
using namespace std;
class Father
{
public:
	void play() {
		cout << "一起去KTV唱歌吧" << endl;
	};
};

class Son :public Father {
public:
	void play() {
		cout << "一起去打游戏吧!" << endl;
	};
};


void testPlay(Father** man, int n) {
	for (int i = 0; i < n; i++) {
		man[i]->play();//man[i] = *man;
	}
}
int main(void) {
	Father father;
	Son son1, son2;
	Father* p[3] = { &father,&son1,&son2 };
	int len = sizeof(p) / sizeof(p[0]);
	testPlay(p, len);
}

?使用多态后(父类方法声明时关键字virtual :虚函数):

是在父类方法声明的时候添加virtual关键字,实现的时候不需要添加

子类重写该方法的时候也可以添加,

父类

?virtual void play() {
?? ?cout << "一起去KTV唱歌吧" << endl;
};

子类

virtual void play() {
?? ?cout << "一起打游戏吧!" << endl;
};

多态的本质?

形式上,使用 统一的父类指针做一般性处理,
但是实际执行时,这个 指针可以指向子类对象
形式上,原本调用父类的方法,但是 实际上会调用子类的同名方法。
注意:
程序执行时,父类指针指向父类对象,或子类对象时,在形式上是无法分辨的!
只有通过多态机制,才能执行真正对应的方法。

?虚函数表(32位下调试)

?

这里一个对象理应内存分布:一个虚函数指针+两个int成员数据(12个字节)

?//虚函数是存放在虚函数表里的
//虚函数表没有占用对象的内存空间,对象内存空间中只有一个虚函数指针
virtual void fun1() {cout << "virtual:fun1()" << endl;}
virtual void fun2() { cout << "virtual:fun2()" << endl; }
virtual void fun3() { cout << "virtual:fun3()" << endl; }

//普通成员函数在代码区
void fun4() { cout << "非虚函数:fun1()" << endl; }

public:
?? ?int x = 200;
?? ?int y = 300;

static int z;//静态成员数据在内存的全局数据区

?测试

?

#include <iostream>
using namespace std;

class Father{
public:
	//虚函数是存放在虚函数表里的
	//虚函数表没有占用对象的内存空间,对象内存空间中只有一个虚函数指针
	virtual void fun1() {cout  << "Father:fun1()" << endl; }
	virtual void fun2() { cout << "Father:fun2()" << endl; }
	virtual void fun3() { cout << "Father:fun3()" << endl; }
	//普通成员在代码区
	void fun4() { cout << "非虚函数:Father::fun4()" << endl; }
	int x = 200;
	int y = 300;
	static int z;//静态成员数据在内存的全局数据区
};
int Father::z = 0;
//定义一个void型函数指针
//virtual void fun1() {cout << "Father:fun1()" << endl;}
typedef void (*fun_t)(void);
int main(void) {
	Father father;
	cout << father.x << endl;
	//一个虚函数大小
	//虚函数表没有占用对象的内存空间,对象内存空间中只有一个虚函数指针,指向虚函数表
	//理应,这里只占12个字节
	cout << "sizeof(father)的大小:" << sizeof(father) << endl;
	cout << "对象的地址" << (int*)&father << endl;
	//先将father的值16进制的地址转换成int*的指针,在取值,然后再转换成int*的指针进行赋值
	int* vptr = (int*)(*(int*)&father);
	cout << "虚函数指针vptr的地址值" << vptr << endl;
	cout << "调用第一个虚函数:";
	((fun_t) * (vptr + 0))();//等同于调用void father::fun1(void);
	cout << "调用第二个虚函数:";
	((fun_t) * (vptr + 1))();//等同于调用void father::fun2(void);
	cout << "调用第三个虚函数:";
	((fun_t) * (vptr + 2))();//等同于调用void father::fun3(void);

	cout << "第一个数据成员的地址:" << endl;
	cout << &father.x << endl;
	cout << std::hex << (int) & father + 4 << endl;
	cout << "第一个数据成员的值:" << endl;
	cout <<std::dec<< father.x << endl;
	cout << *(int*)((int)&father + 4) << endl;

	cout << "第二个数据成员的地址:" << endl;
	cout << &father.y << endl;
	cout << std::hex << (int) & father + 8 << endl;
	cout << "第二个数据成员的值:" << endl;
	cout << std::dec <<father.y << endl;
	cout << *(int*)((int)&father + 8) << endl;
	return 0;
}

子类继承父类虚函数

?子类虚函数的三种状态

完全继承父类?

直接复制父类的虚函数表

子类重写父类的

?在父类虚函数的对应位置进行替换

当父类再次指向子类的时候,子类中的虚函数表已经发生改变,父类这时候调用虚函数得到的就是子类的虚函数表中对应的东西

子类自己的虚函数

将新增的虚函数添加到已有虚函数表尾部

?

?

#include <iostream>
using namespace std;

class Father{
public:
	//虚函数是存放在虚函数表里的
	//虚函数表没有占用对象的内存空间,对象内存空间中只有一个虚函数指针
	virtual void fun1() {cout  << "Father:fun1()" << endl; }
	virtual void fun2() { cout << "Father:fun2()" << endl; }
	virtual void fun3() { cout << "Father:fun3()" << endl; }
	//普通成员在代码区
	void fun4() { cout << "非虚函数:Father::fun4()" << endl; }
	int x = 200;
	int y = 300;
	static int z;//静态成员数据在内存的全局数据区
};
int Father::z = 0;

class Son :public Father {
public:
	//继承父类的虚函数
	virtual void fun1() { cout << "Son:fun1()" << endl; }
	//自己的虚函数
	virtual void fun4() { cout << "Son:fun4()" << endl; }
};


//定义一个void型函数指针
//virtual void fun1() {cout << "Father:fun1()" << endl;}
typedef void (*fun_t)(void);

int main(){
    Son son;
    int* vptr1 = (int*)*(int*)&son;
    cout << "son的地址:" << (int*) & son << endl;
    cout << "sizeof(son)的值:" << sizeof(son) << endl;
    for (int  i = 0; i < 4; i++){
	cout << "调用第" << i + 1 << "个虚函数:";
	    ((fun_t) * (vptr1 + i))();
    }
    for (int i = 1; i <= 2; i++){
	    cout << "第" << i << "个成员的值"<<endl;
	    cout << son.x << endl;
	    cout << *(int*)((int) & son + 4) * i << endl;
    }
    return 0;
}

?使用多重继承的虚函数表

?

#include <iostream>
using namespace std;

class Father{
public:
	//虚函数是存放在虚函数表里的
	//虚函数表没有占用对象的内存空间,对象内存空间中只有一个虚函数指针
	virtual void fun1() {cout  << "Father:fun1()" << endl; }
	virtual void fun2() { cout << "Father:fun2()" << endl; }
	virtual void fun3() { cout << "Father:fun3()" << endl; }
	//普通成员在代码区
	void fun4() { cout << "非虚函数:Father::fun4()" << endl; }
	int x = 200;
	int y = 300;
	static int z;//静态成员数据在内存的全局数据区
};
int Father::z = 0;
//另外一个基类
class Monther {
public:
	//虚函数是存放在虚函数表里的
	//虚函数表没有占用对象的内存空间,对象内存空间中只有一个虚函数指针
	virtual void handel() { cout << "Monther:handl()" << endl; }
	virtual void hande2() { cout << "Monther:handl2()" << endl; }
	//普通成员在代码区
	void fun() { cout << "非虚函数:Father::fun4()" << endl; }
	int m = 400;
	int n = 600;
	static int z;//静态成员数据在内存的全局数据区
};

class Son :public Father ,public Monther{
public:
	//继承父类的虚函数
	virtual void fun1() { cout << "Son:fun1()" << endl; }
	//自己的虚函数
	virtual void fun4() { cout << "Son:fun4()" << endl; }
    //继承另外一个基类的虚函数
    virtual void hande2() { cout << "Son:handl2()" << endl; }
};


//定义一个void型函数指针
//virtual void fun1() {cout << "Father:fun1()" << endl;}
typedef void (*fun_t)(void);

int main(){
    Son son;
    int* vptr1 = (int*)*(int*)&son;
    cout << "第一个虚函数表的指针" << vptr1 << endl;

    //第二个虚函数表
    // 一个虚函数指针,两个成员数据
    int* vptr2 = (int*)*((int*)&son+3);
    cout << "第二个虚函数表的指针" << vptr2 << endl;

    cout << "son的地址:" << (int*) & son << endl;
    cout << "sizeof(son)的值:" << sizeof(son) << endl;
    for (int  i = 0; i < 4; i++){
	cout << "调用第" << i + 1 << "个虚函数:";
	    ((fun_t) * (vptr1 + i))();
    }
	for (int i = 1; i <= 2; i++){
		cout << "第" << i << "个成员的值"<<endl;
		cout << *(int*)((int) & son + 4 * i) << endl;
	}
    for (int i = 1; i <= 2; i++) {
	    cout << "继承母亲的第" << i << "个成员的值" << endl;
	    cout << *(int*)((int)&son +12+ 4 * i) << endl;
    }
    for (int i = 0; i < 2; i++) {
	    cout << "调用母亲的第" << i + 1 << "个虚函数:";
	    ((fun_t) * (vptr2 + i))();
    }
   
    return 0;
}

final关键字

设置该类不能被继承?

?

class i1 {

};
class i2 final :public i1{

};
class i3 :public i2{

};

设置该成员函数不能被重写(final只能用于虚函数中)?

?

?class i1 {
?? ?virtual void chat()final {
?? ??? ?cout << "聊天一代" << endl;
?? ?};
};
class i2 ?:public i1 {
?? ?virtual void chat();
};

override仅能虚函数(仅限函数声明时可以写)

作用:
1. 提示程序的阅读者,这个函数是重写父类的功能。
2. 防止程序员在重写父类的函数时,把函数名写错。

class i1 {
public:
?? ?virtual void chat() {
?? ??? ?cout << "聊天一代" << endl;
?? ?};
};
class i2 ?:public i1 {
public:
?? ?virtual void chat()override;
};

?消失的析构函(析构函数最好加上virtual)

#include <iostream>
#include <string>
using namespace std;

class Father {
public:
	Father(const char* addr = "中国") {
		cout << __FUNCTION__ << endl;
		int len = strlen(addr) + 1;
		this->addr = new char[len];
		strcpy_s(this->addr,len, addr);
	};
	~Father() {
		cout << __FUNCTION__ << endl;
		if (addr) {
			delete[] addr;
			addr = NULL;
		}
	};
private:
	char* addr;
};
class Son:public Father {
public:
	Son(const char* addr = "中国",const char *game = "推箱子"):Father(addr) {
		cout << __FUNCTION__ << endl;
		int len = strlen(game) + 1;
		this->game = new char[len];
		strcpy_s(this->game, len, game);
	};
	~Son() {
		cout << __FUNCTION__ << endl;
		if (game) {
			delete[] game;
			game = NULL;
		}
	};
private:
	char* game;
};
int main(void) {
	string line(50, '-');
	Father* father = new Father();
	delete father;
	cout << line << endl;
	Son* son = new Son();
	delete son;
	cout << line << endl;
	Father* father1 = new Son();
	delete father1;
	cout << line << endl;

}

?上述可以看出

父类的通过子类定义的对象,释放之后,只释放了父类的空间,子类并没有释放;

解决:将父类的析构函数定义为虚函数

作用:当对父类的指针进行delete的时候,就会对该指针使用"动态析构"[如果这个指针是指向子类对象,那么就会先调用该子类的析构函数,然后再调用父类的]

virtual ~Father() {?

?

?纯虚函数和抽象函数

纯虚数(仅提供接口,实际方法由子类实现)

抽象类(拥有纯虚数的的类就是抽象类),抽象类无法创建对象

?

//把eat方法定义为纯虚数
virtual void eat() = 0;

1:纯虚函数一定要被实现才会结束抽象这个类?,

2:要么不对这个纯虚函数做任何处理,等效于下一种情况(该方式不推荐)

3:要么继续把这个纯虚函数声明为纯虚函数,这个子类也成为抽象类virtual void eat() = 0;

#include <iostream>
#include <string>
using namespace std;

class Shape {
public:
	Shape(const string& color = "白色") { this->color = color; }
	string getColor()const {
		return color;
	}
	//定义一个纯虚数
	virtual float area() = 0;
private:
	string color;
};

class Circle :public Shape {
public:
	Circle(float radios, const string& color) :Shape(color),r(radios){};
	//实现父类的纯虚数
	float area() { return 3.14 * r * r; }
private:
	float r;
};
int main(void) {
	//Shape shape;
	Circle cir(2,"白色");
	cout<<cir.getColor()<<endl;
	cout <<cir.area()<<endl;
}

文章来源:https://blog.csdn.net/qq_48397625/article/details/135711319
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。