多态
#include <iostream>
using namespace std;
class Animal{
public:
void speak(){
cout<<"动物在说话"<<endl;
}
};
class Dog:public Animal{
public:
void speak(){
cout<<"狗在汪汪"<<endl;
}
};
int main()
{
//父类指针 保存 子类空间地址
Animal *p=new Dog;
p->speak();
return 0;
}
打印:
动物在说话
其实用户的需求:p->speak 希望等到的是“狗在汪汪” 而不是“动物在说话”。
要实现用户需求则需要通过虚函数来实现
虚函数
成员函数前加virtual修饰
子类重写父类的虚函数注意:有继承、子类重写父类虚函数(函数名、返回值类型、参数类型个数顺序 必须完全一致)。
#include <iostream>
using namespace std;
class Animal{
public:
virtual void speak(){
cout<<"动物在说话"<<endl;
}
};
class Dog:public Animal{
public:
void speak(){
cout<<"狗在汪汪"<<endl;
}
};
int main()
{
Animal *p=new Dog;
p->speak();//打印 狗在汪汪
return 0;
}
多态条件:有继承、子类重写父类的虚函数,父类指针 指向子类空间。
纯虚函数
虚函数不实现函数体
class Animal
{
public:
//纯虚函数
virtual void speak(void)=0;
};
一旦类中有纯虚函数,那么这个类就是抽象类。
抽象类 不能实例化对象。(Object ob;错误)
抽象类 必须被继承 同时 子类 必须重写 父类的所有纯虚函数,否则 子类也是抽象类
抽象类主要的目的是设计类的接口
虚函数和纯虚函数的 区别?
虚函数:virtual修饰 有函数体不会导致父类为抽象类。
纯虚函数:virtual修饰,=0,没有函数体 导致父类为抽象类。子类必须重写父类的所有纯虚函数
虚析构
虚析构:通过父类指针 释放整个子类空间。
#include <iostream>
using namespace std;
class Person{
public:
virtual void fun(){
cout<<"Person的fun()"<<endl;
}
virtual ~Person(){
cout<<"Person中的析构函数"<<endl;
}
};
class Son:public Person{
public:
void fun(){
cout<<"Son类的fun()"<<endl;
}
~Son(){
cout<<"Son的析构函数"<<endl;
}
};
int main()
{
Person *p=new Son;
p->fun();
delete p;
return 0;
}
Son类的fun()
Son的析构函数
Person中的析构函数
构造的顺序:父类—>成员---->子类
析构的顺序:子类—>成员---->父类
纯虚析构函数
纯虚析构的本质:是析构函数,各个类的回收工作。而且析构函数不能被继承。
必须为纯虚析构函数提供一个函数体。纯虚析构函数 必须在类外实现
#include <iostream>
using namespace std;
class Person{
public:
virtual void fun()=0;
virtual ~Person()=0;
};
//纯虚析构函数 必须在类外实现
Person::~Person(){
cout<<"Person中的析构函数"<<endl;
}
class Son:public Person{
public:
void fun(){
cout<<"Son类的fun()"<<endl;
}
~Son(){
cout<<"Son的析构函数"<<endl;
}
};
int main()
{
Person *p=new Son;
p->fun();
delete p;
return 0;
}
重载、重定义、重写的区别
重载:同一作用域,同名函数,参数的顺序、个数、类型不同 都可以重载。函数的返回值类型不能作为重载条件(函数重载、运算符重载)
重定义:有继承,子类 重定义 父类的同名函数(非虚函数), 参数顺序、个数、类型可以不同。子类的同名函数会屏蔽父类的所有同名函数(可以通过作用域解决)
重写(覆盖):有继承,子类 重写 父类的虚函数。返回值类型、函数名、参数顺序、个数、类型都必须一致。