C++入门小馆 :多态
嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的passion。准备好和我一起冲进代码的奇幻宇宙了吗?Let's go!
我的博客:yuanManGan
我的专栏:C++入门小馆 C言雅韵集 数据结构漫游记 闲言碎语小记坊 题山采玉 领略算法真谛
目录
多态的概念:
构成多态的条件:
虚函数:
虚函数的重写/覆盖:
• 协变(了解)
析构函数的重写:
override 和 final关键字
重载/重写/隐藏的对⽐
纯虚函数和抽象类
多态的原理:
虚函数表指针:
虚函数表:
面向对象三大特性:封装,继承,多态。今天来讲讲最后一个多态。
多态的概念:
多态是针对函数来说的,当函数名相同时,我们传入不同的参数会出现不同的效果,这就是多态。多态又分为静态多态,和动态多态,静态指的一般是函数重载和模板(又叫作编译时多态),而我们今天的重点是动态多态(也叫作运行时多态)
动态多态就是,当你传入不同的对象时(函数),会出现不一样的结果,比如我们买火车票,如果你是学生会打折,成人就是全票,而军人就是优先买票。再⽐如,同样是动物叫的⼀个⾏为(函数),传猫对象过去,就是”(>^ω^<) 喵“,传狗对象过去,就是"汪汪"。
构成多态的条件:
这里补充一下:
虚函数:
虚函数的重写/覆盖:
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-打折" << endl; }
};
void Func(Person* ptr)
{// 这⾥可以看到虽然都是Person指针Ptr在调⽤BuyTicket// 但是跟ptr没关系,⽽是由ptr指向的对象决定的。ptr->BuyTicket();
}
int main()
{Person ps;Student st;Func(&ps);Func(&st);return 0;
}
class Animal
{
public:virtual void talk() const{}
};
class Dog : public Animal
{
public:virtual void talk() const{std::cout << "汪汪" << std::endl;}
};
class Cat : public Animal
{
public:virtual void talk() const{std::cout << "(>^ω^<)喵" << std::endl;}
};
void letsHear(const Animal& animal)
{animal.talk();
}
int main()
{Cat cat;Dog dog;letsHear(cat);letsHear(dog);return 0;
}
这里的对虚函数的重写,其实是重写的实现。
• 协变(了解)
class A {};
class B : public A {};
class Person {
public:virtual A* BuyTicket(){cout << "买票-全价" << endl;return nullptr;}
};
class Student : public Person {
public:virtual B* BuyTicket(){cout << "买票-打折" << endl;return nullptr;}
};
void Func(Person* ptr)
{ptr->BuyTicket();
}
int main()
{Person ps;Student st;Func(&ps);Func(&st);return 0;
}
这样写没有很大的意义,我们很少这么实现。
析构函数的重写:

我们定义了两个A类型的指针分别指向A和B对象,但我们析构时为什么没有析构B对象呢?因为我们的p2是指向A类型的,然后我们析构是调用A类型的析构。那我们该怎样调用B的析构函数呢?这里就要用到多态了,但我们的析构的函数名不是不相同吗,怎么实现重写呢?其实编译器会将你写的析构函数的函数名看成一样的Destruct,这就导致我们继承遇到的析构函数为什么会隐藏,这里将A的析构写成隐藏就可以解决了。
override 和 final关键字
override 用来检查一个虚函数是否被重写,没有就会报错。
重载/重写/隐藏的对⽐
纯虚函数和抽象类
在虚函数的后⾯写上 =0 ,则这个函数为纯虚函数,纯虚函数不需要定义实现(实现没啥意义因为要被 派⽣类重写,但是语法上可以实现),只要声明即可。包含纯虚函数的类叫做抽象类,抽象类不能实例 化出对象,如果派⽣类继承后不重写纯虚函数,那么派⽣类也是抽象类。纯虚函数某种程度上强制了 派⽣类重写虚函数,因为不重写实例化不出对象。
多态的原理:
虚函数表指针:

该程序的运行结果(32位的环境下)是什么呢?
有人说是8不就是两个成员吗,内存对齐一下就是8,其实我们有虚函数的类里面还有一个虚函数表指针,指向虚函数表。
所以答案是12。
我们实现虚函数的重写其实就是将虚函数表指针进行重写。使他指向不同的对象,我们无论传入的派生类是怎么样的,传入Func函数都会进行截断,是它只出现父类,然后通过虚函数表指针调用函数,这就形成了多态。
虚函数表:

