- 多态的概念?
通俗来讲,就是一个事物在不同情况下的不同形态。举个例子:你在不同场景下的说话语气。你在家,是父母的小宝贝,你对父母的说话语气;你在学校,是学生,你对老师的说话语气;你在宿舍,是他们的舍友,你的说话语气。 - 多态分类?
有运行时多态(也称动态多态) 和静态多态。
运行时多态是指当代吗运行时,才确认基类的指针或引用具体引用哪个对象(或调用哪个类的成员函数),编译期间无法确认是哪个类的对象,比如说是,虚函数。
静态多态是指在编译器在编译期间就确认了调用的内容,比如,函数重载。 - 重写的概念
是指派生类中有一个跟基类的完全相同虚函数,我们就称子类的虚函数重写了基类的虚函数,完全相同是指:函数名、参数、返回值都相同。(其中有两个例外 1.协变。是返回类型不同 2.析构函数的重写 。函数名的不同) - override和final关键字
.final 修饰基类的虚函数不能被派生类重写。override 修饰派生类虚函数强制完成重写,如果没有重写会编译报错
具体使用如下:
class Car//.final 修饰基类的虚函数不能被派生类重写
{
public:
virtual void Drive() final {}
};
class Benz :public Car
{
public:
virtual void Drive() {cout << "Benz-舒适" << endl;}
};
VS2017中测试结果
-
重写、同名隐藏、函数重载区别
首先说一下函数重载,函数重载,两个必须在同一个作用域里,函数名,参数名必须相同,参数列表必须不同。
同名隐藏和重写都是在继承体系中,前者,眼球基类与派生类具有同名成员函数,且它与虚函数无关。后者要求被重写的函数载基类中必须是虚函数,且函数原型完全一致。 -
什么是抽象类?抽象类特性?一般什么情况下需要将一个类设置成抽象类
我们将包含纯虚函数的类称为抽象类。纯虚函数即为在虚函数的后面加上=0。抽象类不能实例化对象。派生类简单继承后也不能实例化对象,在派生类中必须重写纯虚函数,该派生类才可以实例化对象。有的时候设计一个类时并不需要创建这个类的实例,(或者你无法具体的给出这个类中虚函数的具体内容)声明这个类的目的主要是为了让派生类继承他,并实现抽象的方法,以实现多态的效果,这种情况下需要设计抽象类。 -
虚函数实现原理
简单来说,每个虚函数都有与之相对应的虚函数表,虚函数表的实质是一个指针数组,
该数组中存放的是每个对象的虚函数的地址。派生类会继承基类的虚函数,也会继承虚函数表,同时把自己的虚函数的地址加进去,如果派生类重写了基类的虚函数,则派生类重写的虚函数地址会覆盖掉它从基类中继承下来的虚函数地址,生成自己的虚函数表。 -
虚函数调用原理
虚函数存放在虚函数表中,虚函数表的地址存放在对象的前四个字节中(后面存放的是该对象的成员变量),我们从对象的前四个字节中取出虚函数表的地址,即就是函数指针数组的首地址,再将其转换成函数指针,通过解引用和++,可调用其中的虚函数。
#include<iostream>
#include<string>
using namespace std ;
class Base
{
public :
virtual void TestFunc1 ( )
{
cout << "Base::TestFunc1()" << endl ;
}
virtual void TestFunc2 ( )
{
cout << "Base::TestFunc2()" << endl ;
}
virtual void TestFunc3 ( )
{
cout << "Base::TestFunc3()" << endl ;
}
int _b ;
} ;
class Derived : public Base
{
public :
virtual void TestFunc4 ( )
{
cout << "Derived::TestFunc4()" << endl ;
}
virtual void TestFunc1 ( )
{
cout << "Derived::TestFunc1()" << endl ;
}
virtual void TestFunc3 ( )
{
cout << "Derived::TestFunc3()" << endl ;
}
virtual void TestFunc5 ( )
{
cout << "Derived::TestFunc5()" << endl ;
}
int _d ;
} ;
typedef void ( * PVFT ) ( ) ; //声明函数指针,方便掉用虚函数
void PrintVFT ( Base & b , const string & str )
{
cout << str << endl ;
PVFT * pVFT = ( PVFT * ) ( * ( int * ) & b ) ;//(int *)&b 就是取虚表的地址,即就是函数指针数组的首地址,再将其转换成函数指针
while (* pVFT) //虚表中最后放的是00 00 00 00(我用的vs编译器)
{
(* pVFT) ( ) ; // 解引用就是函数指针数组里面的第一个元素(即就是第一个虚函数地址)
+ + pVFT ; //取下一个
}
cout << endl ;
}
void TestVirtualFunc ( Base & b )
{
b . TestFunc1 ( ) ;
b . TestFunc3 ( ) ;
return;
}
int main ( )
{
Base b ;
Derived d ;
// 打印基类与派生类的虚表
PrintVFT ( b , "Base VFT:" ) ;
PrintVFT ( d , "Derived VFT:" ) ;
// 传递Base类对象
TestVirtualFunc ( b ) ;
cout << endl ;
// 传递派生类对象
TestVirtualFunc ( d ) ;
return 0 ;
}
小白一只,若有错误的地方,欢迎指出:>