目录
- 一.多态
- 1.多态的用处
- 2.多态的实现
- 3.虚函数
- 4.override 和 final
- 5.重载重写与重定义
- 6.虚函数表
一.多态
1.多态的用处
众所周知C++语言的三大特性:封装、多态、继承。其中多态就是去完成某个行为,但是会根据不同的对象产生不同的状态,所以叫多态。
2.多态的实现
在继承中实现多态还需要两个条件:
- 使用基类的指针或者引用来调用虚函数
- 必须在派生类对虚函数进行重写
代码演示如下:
class A
{
public:
virtual void func()
{
cout << "A::func()" << endl;
}
};
class B : public A
{
public:
void func()
{
cout << "B::func()" << endl;
}
};
int main()
{
B b;
A& a = b;
a.func();
return 0;
}
3.虚函数
用virtual修饰的类成员函数叫做虚函数,虽然与虚拟继承用的是同一关键字但是两者并无关联。
virtual void func(){cout << "A::func()" << endl;}
派生类的重写:
重写需要在派生类中编写与基类相同的虚函数:返回类型相同(协变除外)、函数名相同、参数相同(缺省值可不同),然后函数具体实现的不同,来完成多态。
协变
协变允许基类派生类虚函数的返回值不同,但是要求返回值是互为父/子类的指针或者引用。如下所示:
class A
{
public:
A(int a = 1)
:_a(a) {}
virtual A* func(int val = 0)
{
cout << val<< endl;
return new A;
}
private:
int _a;
};
class B : public A
{
public:
B(int b = 1)
:_b(b) {}
virtual B* func(int val = 1)
{
cout << val<< endl;
return new B;
}
private:
int _b;
};
int main()
{
B b;
A& a = b;
a.func();
return 0;
}
上图中,如果返回的不是本身自己父子类的指针/引用,则可以交换顺序!
需要注意的是在派生类重写基类的虚函数可以不加virtual
析构函数的重写
如果基类重写的析构函数,在派生类可以不加virtual也可以完成重写(因为编译器将析构函数的名称统一处理成destructor。)
4.override 和 final
这两个关键字都是C++11的新语法,final修饰虚函数 使得虚函数不可以重写。
override 可以在派生类函数检查是否重写的基类虚函数,如果没有就 报错。
如果不想类被继承可以采用:将构造函数设为私有、将类用final修饰为最终类
5.重载重写与重定义
重载:两个函数在同一作用域、函数名/参数相同
重写:两个函数分别在基类和派生类的作用域、函数名/参数/返回值都必须相同(协变例外)、两个函数必须是虚函数
隐藏(重定义)两个函数分别在基类和派生类的作用域、函数名相同、两个基类和派生类的同名函数不构成重写就是重定义
6.虚函数表
如果在类中定义了虚函数,那么类中就会有个虚表用来存放虚表指针。
虚函数表本质是一个存虚函数指针的指针数组
虚表中存放着虚函数,同类型对象会共用一块虚表。
子类自己的虚函数只会放到第一个父类的虚表后面,其他父类的虚表不需要存储,因为存储了也不能调用
有虚函数的类前4/8字节存储的是虚表的地址。
去虚表找 看是什么对象 (多态时)
虚函数重写只重写函数的实现 缺省值会使用父类的