1:什么是多态?
1.1 概念回顾
虚函数:类的成员函数前面加virtual关键字,则这个成员函数称为虚函数。
虚函数重写:当在子类中定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写(也称覆盖)了父类的这个虚函数。
多态:当使用基类的指针或引用调用重写的虚函数时,当指向父类调用父类的虚函数,当指向子类调用子类的虚函数。
注:
1)派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
2)基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
3)只有类的成员函数才能定义为虚函数。
4)静态成员函数不能定义为虚函数。
5)如果在类外定义虚函数,只能在声明函数时加virtual,类外定义函数时不能加virtual。
6)构造函数不能为虚函数,虽然可以将operator=定义为虚函数,但是最后不要将operator=定义为虚函数,因为容易使用时引起混肴。
7)不要在构造函数和析构函数里面调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会发生未定义的行为。
8)最好把基类的析构函数声明为虚函数。(因为,析构函数比较特殊,派生类的析构函数与基类的析构函数名称不一样,但由于编译器的特殊处理会把析构函数的名称都翻译为deconstruct,就构成覆盖。)
c++中虚函数的主要作用就是去实现多态。即父类的指针/引用调用重写的虚函数,当父类指针/引用指向父类对象调用父类的虚函数,指向子类调用子类的虚函数。
1.2探索虚函数表
虚函数表是通过一块连续内存来存储函数的地址。这张表解决了继承、虚函数(重写)的问题。在有虚函数的对象实例中都存在一张虚函数表,虚函数表就像一张地图,指明了实际应该调用的虚函数函数。
class Base
{
public:
virtual void func1()
{}
virtual void func2()
{}
private:
int a;
};
void Test1()
{
Base b1;
cout<<sizeof(b1)<<endl;
}
分析如下:
由上图可知,Base类的大小由成员变量b1的内存大小和虚表指针所占内存大小决定,故而在32位程序中,b1占8个字节,在64位程序中,b1占12个字节。
1.3 多态的分类——动/静态多态
多态是多种形态,C++的多态分为静态多态和动态多态。
1)静态多态就是重载,因为在编译期间决议确定的,所以称为静态多态。
2)动态多态就是通过继承重写基类的虚函数实现的多态,因为是在运行时决议确定的,所以称为动态多态。
#include<iostream>
using namespace std;
class Base
{
public:
virtual void func1()
{
cout << "Base::func1" << endl;
}
virtual void func2()
{
cout << "Base::func2" << endl;
}
void func3()
{
cout << "Base::func3" << endl;
}
void func3(int n)
{
cout <<n<<"->" "Base::func3" << endl;
}
private:
int a;
};
class Derive :public Base
{
public:
virtual void func1()
{
cout << "Derive::func1" << endl;
}
private:
int b;
};
void Test(Base& p)
{
p.func1();
p.func3();
p.func3(100);
}
int main()
{
Base b;
Derive d;
Test(b);
Test(d);
system("pause");
return 0;
}