目录
1.多态
2.多态成立的条件
虚函数重写:
虚函数重写的两个例外
3.override 和 final
4.重载,重定义(隐藏)和重写
5.抽象类
接口继承和实现继承
6.多态的原理
虚函数表
多态的原理
动态绑定和静态绑定
7.单继承虚表和多继承虚表
单继承虚表的结构
多继承虚表的结构
1.多态
是什么?
多态简单点将就是,不同的对象调用同一个函数结果不同。
虚函数: 非静态成员函数前加virtual修饰,同时virtual和static不能同时存在。
2.多态成立的条件
1.虚函数重写
2.通过基类的指针或引用调用
class Base
{
public:
virtual void func()
{
cout << "Base::func()" << endl;
}
};
class Derived:public Base
{
public:
virtual void func()//虚函数重写
{
cout << "Derived::func()" << endl;
}
//派生类的重写函数前可以不加virtual
//void func()
//{
// cout << "Derived::func()" << endl;
//}
};
void test(Base& b)//基类
{
b.func();//通过基类的引用调用函数
}
//void test(Base* b)
//{
// b->func();通过基类的指针调用
//}
int main()
{
Base b;
Derived d;
test(b);
test(d);
return 0;
}
基类调用基类里的函数,派生类调用派生类中的函数,不同的对象调用,调用出的结果不同。
如果不是通过基类的指针或引用调用,那么就不会构成重写。
void test(Base b)
{
b.func();//不满足多态调用的条件
}
虚函数重写:
派生类中有与virtual修饰的基类的完全相同的函数有(完全相同就是函数名,返回值和参数相同)
注意:虚函数重写的是函数的实现部分
虚函数重写的两个例外
3.override 和 final
final:该虚函数不在被重写
class Base
{
public:
virtual void func()final//虚函数不能重写
{
cout << "Base::func()" << endl;
}
};
class Derived:public Base
{
public:
virtual void func()//会报错,因为无法重写
{
cout << "Derived::func()" << endl;
}
};
override:检查派生类虚函数是否重写,如果没重写则编译报错
class Base
{
public:
void func()
{
cout << "Base::func()" << endl;
}
};
class Derived:public Base
{
public:
void func()override//编译报错,因为不是重写
{
cout << "Derived::func()" << endl;
}
};
4.重载,重定义(隐藏)和重写
重写和重定义的关系:重写是一种特殊的重定义
5.抽象类
纯虚函数:在虚函数后加上 =0
virtual void func() = 0;//纯虚函数
抽象类:就是有纯虚函数的类。
//抽象类
class A
{
public:
virtual void func() = 0;
};
抽象类不能实例化出对象,只有重写纯虚函数的类才能实例化出对象,如果该派生类没有重写纯虚函数,那么该派生类也是一个抽象类,也无法实例化出对象。
class A
{
public:
virtual void func() = 0;
};
class B:public A
{
public:
virtual void func()
{
cout << "B:func()" << endl;
}
};
int main()
{
A a;//错误,抽象类无法实例化出对象
B b;//B派生类,重写虚函数,可以实例化出对象
return 0;
}
接口继承和实现继承
普通函数的继承就是实现继承;纯虚函数和虚函数的重写就是接口的继承,重写的是虚函数的实现部分。
6.多态的原理
虚函数表
class A
{
public:
virtual void func()//虚函数
{
cout << "hello" << endl;
}
private:
int _a;
};
int main()
{
A a;
return 0;
}
a的类中又一个_vfptr,类型是一个void**的二级指针。_vfptr(v是virtual,f是function)是虚函数表指针,而虚表的本质就是存放虚函数指针的指针数组。
class Base
{
virtual void func1()
{
cout << "Base::func1()";
}
virtual void func2()
{
cout << "Base::func2()";
}
};
class Derived :public Base
{
virtual void func1()
{
cout << "Derived::func1()";
}
};
int main()
{
Base b;
Derived d;
return 0;
}
总结:虚函数指针存在类中,指向虚函数表,虚函数表本质就是数组 ,存的是指向虚函数的函数指针。
多态的原理
void Func(Base& b)
{
b.func1();
cout << endl;
}
int main()
{
Base b;
Derived d;
Func(b);
Func(d);
return 0;
}
结果:
在调用func1时是通过类中的虚表指针调用虚表,再通过虚函数表调用相应的函数,Derived中的func1是重写过的,其虚函数表中的相应的指针会被修改了,指向Derived::func1()这个重写后的函数。
简单点说就是虚函数被重写后改变了虚表中指向基类中该虚函数的元素的值,使其指向派生类中重写的虚函数。
动态绑定和静态绑定
静态绑定:就是在编译时就确定了调用哪个函数,比如类中的普通函数。
动态绑定:就是在运行时确定调用哪个函数,就像多态,是通过虚表确定调用哪个函数。
7.单继承虚表和多继承虚表
单继承虚表的结构
多继承虚表的结构