分类:
静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名。 编译阶段确定函数地址
动态多态: 派生类和虚函数实现运行时多态 运行阶段确定函数地址
C++可以进行 父子之间的类型转换,一个全局函数形参是父类,可以生成一个子类对象直接传入作为实参,但是此时对于父类子类的同名函数,首先应该调用的是父类的成员函数,因为此情况属于地址早绑定(因为该全局函数的参数是父类地址,在编译阶段就确定)
解决该情况就应该使用虚函数构建动态多态,在父类同名函数前加入virtual,使其在运行阶段再确定函数地址,根据传入对象的不同来确定不同的地址
(重写的概念:不同于重载,其函数返回值类型、函数名、参数列表都完全相同)
此处要保证子类成员函数于和父类成员函数是重写的关系
上述动态多态的使用条件:父类指针或者引用指向子类对象 Base * p = new Son; delete p;
虚函数的内存占用:
对于如下一个示例类:
class animal {
public:
void speak()
{
cout <<"111"<<endl;
}
};
其内存占用为1,因为属于一个空类,其成员函数不属于类的内存空间(非静态成员函数、静态成员函数、静态成员变量都不属于)
此时其成员函数若变为虚函数:
class animal {
public:
virtual void speak()
{
cout <<"111"<<endl;
}
};
则该类的内存占用为4,因为本质是一个指针(指针无论什么类型都是占用4字节(32位系统、VS编译)),如果是64位系统,占用8字节
纯虚函数和抽象类:
纯虚函数来源于:父类中的虚函数一般不会被实例化、无意义,主要都是调用子类重回的内容,则其可以写为纯虚函数:
virtual 返回值类型 函数名(参数列表) = 0 ;
如果一个类中有纯虚函数,则类也成为抽象类
抽象类无法被实例化、其子类必须要重写抽象类中的纯虚函数,否则也属于抽象类,无法实例化
理解为虚函数存在的意义就在于让子类重写该函数
虚析构和纯虚析构:
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
父类指针在析构的时候,不会调用子类中的析构函数,导致子类如果在堆区有属性会内存泄漏
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
virtual ~Base( ) { ... }
虚析构和纯虚析构共性:
1、可以解决父类指针释放子类对象
2、都需要有具体的函数实现虚析构和纯虚析构区别:
如果是纯虚析构,该类同样属于抽象类,同样无法实例化对象
纯虚析构:
virtual ~Base( ) =0;
但是与前面纯虚函数不同的是,纯虚析构和虚析构不仅要有代码声明也要有代码具体内容实现