1、什么是运行期多态?
在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。 给出一个简单的代码:
#include<iostream>
using namespace std ;
class CBase
{
public:
void FunTest1(int _iTest)
{
cout << "CBase::FunTest1()" << endl;
}
virtual void FunTest3(int _iTest)
{
cout << "CBase:: FunTest3()" << endl;
}
virtual void FunTest4(int _iTest)
{
cout << "CBase:: FunTest4()" <<endl;
}
};
class CDerived: public CBase
{
void FunTest1(int _iTest)
{
cout << "CDerived::FunTest1()" << endl;
}
virtual void FunTest3(int _iTest)
{
cout << "CDeived:: FunTest2()" << endl;
}
void FunTest4(int _iTest)
{
cout << "CDerived:: FunTest3()" << endl;
}
};
void FunTest(CBase& cobj)
{
cobj.FunTest1(1);
cobj.FunTest3(1);
cobj.FunTest4(1);
}
int main()
{
CBase c;
cout << "父类对象去调用函数" <<endl;
FunTest(c); //父类对象
cout << endl;
cout << "子类对象去调用函数" <<endl;
CDerived d; //子类对象
FunTest(d);
cout << "hello..." <<endl;
system("pause");
return 0;
}
结果分析:我们发现不管通过子类对象还是父类对象做参数,对FunTest1()函数的调用都是调用父类的的函数,没有表现出多态,但是其他两个函数却能调用各自的函数,这就是多态。这里就简单的总结下多态发生的三个条件:
1)、要有继承
2)、父类的虚函数要在子类中重写。
3)、父类的指针或引用指向父类对象或子类对象。
2 多态是怎么实现的?
对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针 ,子类对象和父类对象都有各自的虚表,虚表存放的是各自虚函数的入口地址,当通过基类的指针或引用调用虚函数时,系统会通过动态对象的指针找到对应的虚表,拿到相对应的函数的入口地址,实现多态。
#include<iostream>
using namespace std ;
class B
{
public:
virtual void Funtest1()
{
cout << "B::Funtest1()" << endl;
}
virtual void Funtest2()
{
cout << "B::Funtest2()" << endl;
}
public:
int _data1;
};
class D:public B
{
public:
virtual void Funtest1()
{
cout << "D::Funtest1()" << endl;
}
virtual void Funtest3()
{
cout << "D::Funtest3()" << endl;
}
int _data2;
};
void test0()
{
B b1;
b1._data1 = 1;
cout << "sizeof(b1)=" << sizeof(b1) << endl;
typedef void(*PVF)(); //虚表中存放的都是无返回,无参数的函数,定义一个这种类型的函数指针去指向它们。
PVF *pvf = (PVF*)*(int*)&b1;//对象的前四个字节存放一个指针,这个指针指向一个虚表,虚表中存放的都是函数入口地址。
//这一句就相当于一个函数指针类型的指针去指向一个函数指针类型的数组。
cout << "基类虚表:" <<endl;
while (*pvf)
{
(*pvf)(); //通过这个循环打印出虚表中存放的函数。
pvf++;
}
cout << endl;
D d1;
d1._data1 = 2;
d1._data2 = 3;
cout << "sizeof(d1)=" << sizeof(d1) << endl;
pvf = (PVF*)*(int*)&d1;//对象的前四个字节存放一个指针,这个指针指向一个虚表,虚表中存放的都是函数入口地址。
//这一句就相当于一个函数指针类型的指针去指向一个函数指针类型的数组。
cout << "派生类虚表:"<<endl;
while (*pvf)
{
(*pvf)(); //通过这个循环打印出虚表中存放的函数。
pvf++;
}
}
int main()
{
test0();
system("pause");
return 0;
}
(2)多继承:
#include<iostream>
using namespace std ;
class C1
{
public:
virtual void Funtest1()
{
cout << "C1::Funtest1()" << endl;
}
virtual void Funtest2()
{
cout << "C1::Funtest2" <<endl;
}
public:
int _data2;
};
class C2
{
public:
virtual void Funtest3()
{
cout << "C2::Funtest1()" << endl;
}
virtual void Funtest4()
{
cout << "C2::Funtest3" <<endl;
}
public:
int _data3;
};
class D:public C1,public C2
{
public:
virtual void Funtest4()
{
cout << "D::Funtest3()" << endl;
}
virtual void Funtest2()
{
cout << "D::Funtest2" <<endl;
}
virtual void Funtest7() //派生类的虚函数。
{
cout << "D::Funtest7" <<endl;
}
virtual void Funtest8() //派生类的虚函数。
{
cout << "D::Funtest8" <<endl;
}
public:
int _data4;
};
void test()
{
cout << "sizeof(D):" << sizeof(D) << endl; //sizeof(D):28 c1 12,+c2 12,+d 4 = 28
D d1;
cout << "&d1" << &d1 <<endl; //看下派生类的地址。
d1._data2 = 2;
d1._data3 = 3;
d1._data4 = 4;
C1 &c1 = d1;
cout << "&c1" << &c1 <<endl; //看下基类去引用一个派生类,基类的地址。
typedef void (*PVF)();
PVF *pvf = (PVF*)*(int*)&c1; //这就相当于一个二级指针指向一个指针数组。
cout << "c1 virtural table:" << endl;
while (*pvf)
{
(*pvf)();
pvf++;
}
cout << endl << endl;
C2 &c2 = d1; //c2的地址就是d1偏移C1类型的地址。
cout << "&c2" << &c2 <<endl; //看下基类去引用一个派生类,基类的地址。
pvf = (PVF*)*(int*)&c2; //这就相当于一个二级指针指向一个指针数组。
cout << "c2 virtural table:" << endl;
while (*pvf)
{
(*pvf)();
*pvf++;
}
cout << endl;
}
int main()
{
test();
cout << "hello..." <<endl;
system("pause");
return 0;
}
说明:多继承的时候派生类中有两张虚表,这里先继承C1,在继承C2,因此第一张虚表存放的是从C1拷贝过来虚表,如果子类有虚函数重写,将用重写的虚函数代替原来的虚函数,通过结果发现派生类的虚函数也放在第一张虚表中,第二张虚表存放的是从C2拷贝过来的虚表,同样也是用重写的虚函数代替原来的虚函数。
(3)菱形继承:
#include<iostream>
using namespace std ;
class B
{
public:
virtual void Funtest1()
{
cout << "B::Funtest1()" << endl;
}
public:
int _data1;
};
class C1:public B
{
public:
virtual void Funtest1()
{
cout << "C1::Funtest1()" << endl;
}
virtual void Funtest2()
{
cout << "C1::Funtest2" <<endl;
}
public:
int _data2;
};
class C2:public B
{
public:
virtual void Funtest1()
{
cout << "C2::Funtest1()" << endl;
}
virtual void Funtest3()
{
cout << "C2::Funtest3" <<endl;
}
public:
int _data3;
};
class D:public C1,public C2
{
public:
virtual void Funtest3()
{
cout << "D::Funtest3()" << endl;
}
virtual void Funtest4()
{
cout << "D::Funtest4" <<endl;
}
public:
int _data4;
};
void test()
{
cout << "sizeof(D):" << sizeof(D) << endl; //sizeof(D):28 c1 12+c2 12+d 4 = 28
D d1;
d1.C1::_data1 = 0;
d1.C2::_data1 = 1;
d1._data2 = 2;
d1._data3 = 3;
d1._data4 = 4;
C1 &c1 = d1;
typedef void (*PVF)();
PVF *pvf = (PVF*)*(int*)&c1; //这就相当于一个二级指针指向一个指针数组。
cout << "c1 virtural table:" << endl;
while (*pvf)
{
(*pvf)();
pvf++;
}
cout << endl << endl;
C2 &c2 = d1;
pvf = (PVF*)*(int*)&c2; //这就相当于一个二级指针指向一个指针数组。
cout << "c2 virtural table:" << endl;
while (*pvf)
{
(*pvf)();
*pvf++;
}
cout << endl;
}
int main()
{
test();
cout << "hello..." <<endl;
system("pause");
return 0;
}
说明:菱形继承的时候派生类中有两张虚表,这里先继承C1,在继承C2,因此第一张虚表存放的是从C1拷贝过来虚表,如果子类有虚函数重写,将用重写的虚函数代替原来的虚函数,通过结果发现派生类的虚函数也放在第一张虚表中,第二张虚表存放的是从C2拷贝过来的虚表,同样也是用重写的虚函数代替原来的虚函数。菱形继承有二义性,都继承了B的数据成员。
二:虚继承
(1)单继承