目录
知识点1【多态的概述】(了解)
1、多态的分类:静态多态、动态多态
静态多态(静态联编、编译时多态):在编译的时候 就确定了函数的入口地址。(函数重载、运算符重载)
动态多态(动态联编、运行时多态):在运行的时候 就确定了函数的入口地址。(虚函数)
2、虚函数的引入
#include <iostream>
using namespace std;
class Animal
{
public:
void speak(void)
{
cout<<"动物在唱歌"<<endl;
}
};
class Dog:public Animal
{
public:
void speak(void)
{
cout<<"狗在汪汪"<<endl;
}
};
void test01()
{
//基类指针 指向 子类对象空间
Animal *p = new Dog;
p->speak();//结果是动物在唱歌
}
int main(int argc, char *argv[])
{
test01();
return 0;
}
//动物在唱歌
3、动态多态(重要)
1、有继承
2、基类指针 指向 子类空间
3、基类的"同名"函数必须虚函数
4、子类重写(同名、同参、同返回值类型,不同函数功能)基类的虚函数。
这样 才能使用基类指针 操作 子类的"同名函数"
#include <iostream>
using namespace std;
class Animal
{
public:
//虚函数
virtual void speak(void)
{
cout<<"动物在唱歌"<<endl;
}
};
class Dog:public Animal
{
public:
//子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
virtual void speak(void)
{
cout<<"狗在汪汪"<<endl;
}
};
void test01()
{
//基类指针 指向 子类对象空间
Animal *p = new Dog;
//必须要用基类指针 操作子类的同名函数
p->speak();
}
int main(int argc, char *argv[])
{
test01();
return 0;
}
//狗在汪汪
#include <iostream>
using namespace std;
class Animal
{
public:
//虚函数
virtual void speak(void)
{
cout<<"动物在唱歌"<<endl;
}
};
class Dog:public Animal
{
public:
//子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
virtual void speak(void)
{
cout<<"狗在汪汪"<<endl;
}
};
class Cat:public Animal
{
public:
//子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
virtual void speak(void)
{
cout<<"猫在喵喵"<<endl;
}
};
class Mouse:public Animal
{
public:
//子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
virtual void speak(void)
{
cout<<"老鼠在吱吱"<<endl;
}
};
void animalSpeak(Animal &ob)
{
ob.speak();
}
void test02()
{
Dog dog;
Cat cat;
Mouse mouse;
animalSpeak(dog);
animalSpeak(cat);
animalSpeak(mouse);
}
int main(int argc, char *argv[])
{
test02();
return 0;
}
//狗在汪汪
//猫在喵喵
//老鼠在吱吱
4、虚函数的原理(了解)
1、分析Animal类
class Animal
{
public:
//虚函数
virtual void speak(void)
{
cout<<"动物在唱歌"<<endl;
}
};
当拥有虚函数的基类 不涉及到继承时,会产生一个vfptr(virtual function ptr)虚函数指针,该虚函数指针指向了一张虚函数表(vftable), vftable保存基类的同名函数入口地址。
2、分析Dog类:
当涉及到继承时:基类的虚函数指针、虚函数表 都会被继承到子类中,如果子类重写 基类的虚函数。会将虚函数表中的入口地址 更改成 子类的“同名”函数。所以这时使用基类指针调用的函数 是子类的同名函数。
拓展:
知识点2【纯虚函数】(了解)
在基类中 不实现虚函数的函数体。这样的虚函数 叫纯虚函数。
class Animal
{
public:
//纯虚函数 =0 不实现函数体
virtual void speak(void)=0;
};
如果一个类中 只要有一个纯虚函数 那么这个类 是抽象类。
抽象类 不能实例化对象。
Animal ob;//err 抽象类 不能实例化对象
抽象基类 派生出子类 ,子类必须重写基类的所有纯虚函数。否则子类 也是抽象类。
#include <iostream>
using namespace std;
class Animal
{
public:
//纯虚函数 =0 不实现函数体
virtual void speak(void)=0;
};
class Dog:public Animal
{
public:
//子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
virtual void speak(void)
{
cout<<"狗在汪汪"<<endl;
}
};
void test01()
{
//Animal ob;//err 抽象类不能实例化对象
//基类指针 指向 子类对象空间
Animal *p = new Dog;
//必须要用基类指针 操作子类的同名函数
p->speak();
}
int main(int argc, char *argv[])
{
test01();
return 0;
}
知识点3【重载、重定义(隐藏)、重写(覆盖)】(了解)
重载:没有继承、函数重载、运算符重载(静态多态)
重定义:必须有继承,子类重定义父类的非虚函数(参数类型、个数、顺序,函数的返回值类型都可以不同)。(静态多态)
重写:必须有继承,子类重写父类的虚函数(参数类型、个数、顺序,返回值类型 必须一致)(动态多态)
知识点4【多态案例】(了解)
#include <iostream>
using namespace std;
//抽象制作饮品
class AbstractDrinking{
public:
//烧水
virtual void Boil() = 0;
//冲泡
virtual void Brew() = 0;
//倒入杯中
virtual void PourInCup() = 0;
//加入辅料
virtual void PutSomething() = 0;
//规定流程
void MakeDrink(){
this->Boil();
Brew();
PourInCup();
PutSomething();
}
};
class Coffee : public AbstractDrinking{
public:
//烧水
virtual void Boil(){
cout << "煮农夫山泉!" << endl;
}
//冲泡
virtual void Brew(){
cout << "冲泡咖啡!" << endl;
}
//倒入杯中
virtual void PourInCup(){
cout << "将咖啡倒入杯中!" << endl;
}
//加入辅料
virtual void PutSomething(){
cout << "加入牛奶!" << endl;
}
};
//制作茶水
class Tea : public AbstractDrinking{
public:
//烧水
virtual void Boil(){
cout << "煮自来水!" << endl;
}
//冲泡
virtual void Brew(){
cout << "冲泡茶叶!" << endl;
}
//倒入杯中
virtual void PourInCup(){
cout << "将茶水倒入杯中!" << endl;
}
//加入辅料
virtual void PutSomething(){
cout << "加入食盐!" << endl;
}
};
//业务函数
void DoBussiness(AbstractDrinking* drink){
drink->MakeDrink();
delete drink;
}
void test(){
DoBussiness(new Coffee);
cout << "--------------" << endl;
DoBussiness(new Tea);
}
int main(int argc, char *argv[])
{
test();
return 0;
}
知识点5【虚析构函数】(了解)
1、知识点的引入
#include <iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout<<"Animal的无参构造"<<endl;
}
//纯虚函数 =0 不实现函数体
virtual void speak(void)=0;
~Animal()
{
cout<<"Animal析构函数"<<endl;
}
};
class Dog:public Animal
{
public:
Dog()
{
cout<<"Dog的无参构造"<<endl;
}
//子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
virtual void speak(void)
{
cout<<"狗在汪汪"<<endl;
}
~Dog()
{
cout<<"Dog的析构函数"<<endl;
}
};
void test01()
{
//Animal ob;//err 抽象类不能实例化对象
//基类指针 指向 子类对象空间
Animal *p = new Dog;
//必须要用基类指针 操作子类的同名函数
p->speak();
delete p;
}
int main(int argc, char *argv[])
{
test01();
return 0;
}
2、虚析构(解决上述问题)
#include <iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout<<"Animal的无参构造"<<endl;
}
//纯虚函数 =0 不实现函数体
virtual void speak(void)=0;
virtual ~Animal()
{
cout<<"Animal析构函数"<<endl;
}
};
class Dog:public Animal
{
public:
Dog()
{
cout<<"Dog的无参构造"<<endl;
}
//子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
virtual void speak(void)
{
cout<<"狗在汪汪"<<endl;
}
~Dog()
{
cout<<"Dog的析构函数"<<endl;
}
};
void test01()
{
//Animal ob;//err 抽象类不能实例化对象
//基类指针 指向 子类对象空间
Animal *p = new Dog;
//必须要用基类指针 操作子类的同名函数
p->speak();
delete p;
}
int main(int argc, char *argv[])
{
test01();
return 0;
}
3、虚析构的原理(了解)
Animal的类的布局:
Dog的类的布局
总结:虚函数和虚析构的区别?
虚函数:
虚函数一般指的是虚成员函数,成员函数名前加virtual修饰,需要实现函数体,子类必须重写 父类的虚函数(函数的参数个数、顺序、类型,返回值类型必须完全一直)。 用户可以使用父类指针 操作 子类重写的虚函数。
虚析构:
父类的析构函数名前加virtual修饰,需要实现函数体,子类只需实现子类的析够函数即可,用户使用delete释放 父类指针 达到释放整个子类空间的效果。
知识点6【纯虚析构函数】(了解)
虚析构:
class Base
{
public:
virtual ~Base()
{
//函数体
}
}
纯虚析构:
class Base//抽象类
{
public:
virtual ~Base()=0
{
//必须有函数体
}
}
纯虚析构 函数和非纯析构函数不同点:
纯虚析构函数使得基类是抽象类,不能创建基类的对象。