目录
3、抽象类
3.1概念
在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
抽象类在系统编程时用的比较广泛,Windows系统为开发者提供了三千多个API函数,这些函数能够直接操作Windows系统,但是这么多的接口函数,记忆起来相当困难,于是微软就提供了比如MFC框架应用程序,将这几千个API 封装成好几个大的基类,每个基类包含了不同领域的功能。在这些基类当中,继承、多态使用的非常广泛,学好这些基本概念,对理解mfc有很大的帮助。
3.2、接口继承和实现继承
普通的继承是实现继承,继承了基类成员函数的功能,虚函数的继承是接口继承,就是提供了一个入口,让派生类去重写,实现新的功能,但是一般常用,所以如果不是为了实现多态,最好不要用虚函数。
4、多态的原理:虚函数表
这里不卖关子了,直接开门见山吧。
每个包含虚函数的类,都会有一个函数指针数组去管理它众多的虚函数,当然普通函数根本不占用类的内存空间存在公共区域,虚表不会管理它。
4.1、单继承
基类虚表会通过函数指针数组维护自己的虚表,派生类也有一张虚表管理继承下来的虚函数,和自己的虚函数。
我们来看
#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;
}
private:
int _b = 1;
};
class Derive : public Base
{
public:
virtual void Func1()
{
cout << "Derive::Func1()" << endl;
}
virtual void derive()
{
cout << "deriver" << endl;
}
private:
int _d = 2;
};
int main()
{
Base b;
Derive d;
system("pause");
return 0;
}
class Base size(8):
1> +---
1> 0 | {vfptr}
1> 4 | _b
1> +---
1>
1>Base::$vftable@:
1> | &Base_meta
1> | 0
1> 0 | &Base::Func1
1> 1 | &Base::Func2
1>
1>Base::Func1 this adjustor: 0
1>Base::Func2 this adjustor: 0
1>
1>class std::is_lvalue_reference<class std::basic_ostream<char,struct std::char_traits<char> > &> size(1):
1> +---
1> 0 | +--- (base class std::integral_constant<bool,1>)
1> | +---
1> +---
1>
1>class std::is_error_code_enum<char const *> size(1):
1> +---
1> 0 | +--- (base class std::integral_constant<bool,0>)
1> | +---
1> +---
1>
1>class Derive size(12):
1> +---
1> 0 | +--- (base class Base)
1> 0 | | {vfptr}
1> 4 | | _b
1> | +---
1> 8 | _d
1> +---
1>
1>Derive::$vftable@:
1> | &Derive_meta
1> | 0
1> 0 | &Derive::Func1
1> 1 | &Base::Func2
1> 2 | &Derive::derive
1>
1>Derive::Func1 this adjustor: 0
1>Derive::derive this adjustor: 0
打印出虚表信息,清楚地看到,虚表名字叫vfptr,主要看派生类
重写了基类的虚函数,和继承基类的虚函数,最后是自己的虚函数。
4.2、多继承
#include <iostream>
using namespace std;
class Base1 {
public:
virtual void func1() { cout << "Base1::func1" << endl; }
virtual void func2() { cout << "Base1::func2" << endl; }
private:
int b1;
};
class Base2 {
public:
virtual void func1() { cout << "Base2::func1" << endl; }
virtual void func2() { cout << "Base2::func2" << endl; }
private:
int b2;
};
class Derive : public Base1, public Base2 {
public:
virtual void func1() { cout << "Derive::func1" << endl; }
virtual void func3() { cout << "Derive::func3" << endl; }
private:
int d1;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{
cout << " 虚表地址>" << vTable << endl;
for (int i = 0; vTable[i] != nullptr; ++i)
{
printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
VFPTR f = vTable[i];
f();
}
cout << endl;
}
int main()
{
Derive d;
VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);
PrintVTable(vTableb1);
VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));
PrintVTable(vTableb2);
system("pause");
return 0;
}
我们可以很清楚的看到基类和继承了两个基类的派生类的虚表,我么重点来看一下派生类的虚表,不出所料,派生类也只有一个虚表, 但是他的虚表里面维护的是基类1和基类2的虚表,也就是说它里面存的是函数指针数组,他自己的虚函数在第一个基类的虚表里面维护。
并且,派生类重写两个基类的虚函数,第二个派生类虚表里面用goto 直接指向了基类1里面的重写后的虚函数。
4.3、动态绑定和静态绑定
静态绑定(早绑定):在程序的编译阶段就应经确定了程序的行为,调用具体的函数,也称为静态多态,比如函数的重载
动态绑定(迟绑定):在程序运行期间,根据具体拿到的类型确定程序的行为,调用具体的函数称为动态多态。
5、菱形继承
#include <iostream>
using namespace std;
class base
{
virtual void func()
{
cout << "base" << endl;
}
virtual void funcbase()
{
cout << "funcbase" << endl;
}
};
class base1 :public base
{
virtual void func()
{
cout << "base1" << endl;
}
virtual void funbase1()
{
cout << "funbase1" << endl;
}
};
class base2 :public base
{
virtual void func()
{
cout << "base2" << endl;
}
virtual void funcbase2()
{
cout << "funcbase2" << endl;
}
};
class son :public base1, public base2
{
virtual void func()
{
cout << "son" << endl;
}
virtual void funcson()
{
cout << "funcson" << endl;
}
};
int main()
{
base grandfarth;
base1 farth1;
base2 farth2;
son Son;
system("pause");
return 0;
}
虚函数表:
通过上面的介绍,base1 类和base2 类的虚表相信大家已经清楚了,直接看son类的虚表。
菱形继承:
虚继承:
#include <iostream>
using namespace std;
class A
{
public:
int num;
A()
{
num = 100;
cout << "A" << endl;
}
~A()
{
cout << "~A" << endl;
}
};
class B :virtual public A //虚继承
{
public:
B()
{
cout << "B" << endl;
}
~B()
{
cout << "~B" << endl;
}
};
class C :virtual public A //虚继承
{
public:
C()
{
cout << "C" << endl;
}
~C()
{
cout << "~C" << endl;
}
};
class D :public B, public C
{
public:
D()
{
cout << "D" << endl;
}
~D()
{
cout << "~D" << endl;
}
};
int main()
{
D d;
//cout<<d.num<<endl;//error:访问不明确;没加virtual前;
//第一种方案:清楚的表明作用域,以免出现歧义:
//cout<<d.B::num<<endl;
//cout<<d.C::num<<endl;
//第二种方案:加上virtual后,就没有歧义了,B和C都指向同一份A;
cout << d.num << endl;//加上virtual OK
system("pause");
return 0;
}