类多态

一、多态的基本概念

多态的本质就是 “定义接口与实现分离”,父类制定“要做什么”(契约),子类决定“具体怎么做”(实现)

原先,有一个指向基类的指针,它不知道派生类有哪些东西,所以派生类中的成员,是没有办法通过这个指针来调用的;但是如果我们在基类中定义一个接口(虚函数),然后在派生类中各自去实现这个接口(分离),这样我们就可以了!

几点注意:

  • 虚函数(Virtual Function):在基类中使用 virtual关键字声明的成员函数。派生类可以重写(覆盖)该函数,提供自己的实现。
  • 动态绑定(Dynamic Binding):程序运行时根据指针或引用实际指向的对象类型来决定调用哪个函数,而不是编译时根据指针类型决定。
  • 基类指针/引用:可以指向基类或派生类对象,通过虚函数实现多态调用。
  • 默认继承:如果派生类没有重写虚函数,则调用基类版本。
  • 虚函数声明:只需在基类函数声明时加 virtual,定义时不能加
  • 使用类名和域解析符 ::强制调用基类虚函数。
  • 重写函数的时候,注意所有特征都需要一致!
我的理解,这个东西就是在父类中提供一个接口的东西,然后在子类中具体实现;比如说一个空调父类,那么可以在父类中搞个制冷;但是怎么具体制冷,需要美的、格力这些子类去实现
#include <iostream>
using namespace std;

class Animal {
public: 
	virtual void make_sound() {
		cout<<"Animal's sound\n";
	} // 虚函数,可以在派生类中重写
	// 是一个接口,具体怎么实现由派生类去写
};

class Dog: public Animal {
public: 
	void make_sound() {
		cout<<"wangwangwang\n";
	}
};

class Cat: public Animal {
public: 
	void make_sound() override { // 加 override 明确表示重写了!
		cout<<"miaomiaomiao\n";
	}
};
int main() {
	Animal* ptr=nullptr;
	Dog d;
	ptr=&d;
	ptr->make_sound();
	ptr->Animal::make_sound();

	Cat c;
	Animal& yy=c;
	yy.make_sound();

	Animal a;
	ptr=&a;
	ptr->make_sound();
	return 0;
}

二、一些细节

  • 构造函数不能声明为虚函数以支持多态!

  • 基类析构函数常声明为虚函数以避免资源泄漏!就是在父类的析构函数加个 virtual,这样当一个父类指针指向子类的时候,才能完成先调用派生类的析构函数、再调用基类的析构函数。

三、纯虚函数

纯虚函数是一种特殊的虚函数,在某些情况下,基类中不能对虚函数给出有意义的实现,把它声明为纯虚函数。(跟 python 中的抽象方法类似,提供接口)

纯虚函数=说清楚我在基类中只提供一个接口!不会有啥用!不需要函数体!

virtual 返回值类型 函数名 (参数列表)=0;

含有纯虚函数的类被称为抽象类,不能实例化对象,可以创建指针和引用。

四、dynamic_cast

只适用于包含虚函数的类。

基类指针可以指向派生类对象,如何知道基类指针指向的是哪种派生类的对象呢?(想调用派生类中的非虚函数)。因为你不知道这个非虚函数存不存在!
最简单的例子;动物,猫、狗;但是现在给你一个指向动物的指针,你知道它会不会吃屎嘛(是不是狗)

delete 的时候只需要之前那个指针,因为两个指针指向同一块内存,同一块内存不能两次还给操作系统,还完一次之后,这个内存就不是你的了!

语法:派生类指针 = dynamic_cast<派生类类型 *>(基类指针);
如果转换成功,dynamic_cast返回对象的地址,如果失败,返回nullptr。
#include <iostream>
using namespace std;

class Father {
public:
	virtual void sing() {
		cout<<"hi\n";
	}
};
class Son1:public Father {
public:
	void sing() override {
		cout<<"I'm son1\n";
	}
	void show() {
		cout<<"I'm unique!!!!!\n";
	}
};
class Son2:public Father {
public:
	void sing() override {
		cout<<"I'm son2\n";
	}
};

int main() {
	Father* ptr=nullptr;
	ptr=new Son1;
	ptr->sing();
	// 但是你不知道 ptr 能不能 show!
	// 转换!
	Son1* new_ptr=dynamic_cast<Son1*>(ptr);
	if(new_ptr!=nullptr) new_ptr->show();
	delete ptr;
	return 0;
}

五、关于 delete

delete 其实就是把申请的内存还给操作系统,那么原先的指针指向这块内存,但是这块内存已经徒有虚名了!所以这个指针变成了野指针,你要将其置为 nullptr

六、typeid

利用 typeid(数据类型/变量名).name() 能获取一个字符串,表示这个是啥!

#include <iostream>
using namespace std;

class A {
public :
};

int main() {
	int a=3;
	cout<<typeid(a).name()<<'\n';
	double f=0.5;
	cout<<typeid(f).name()<<'\n';
	int* ptr=&a;
	cout<<typeid(ptr).name()<<'\n';
	A aa;
	cout<<typeid(aa).name()<<'\n';
	return 0;
}
posted @ 2026-02-20 12:51  FxorG  阅读(16)  评论(0)    收藏  举报