1.多态:多种形式或形态的情形。
2.多态的分类:<1>.静态多态:发生在编译阶段->函数重载、泛型编程。
<2>.动态多态:程序运行时。动态多态实现的两个条件:a.调用函数必须是虚函数**(一定要在派生类中对基类的虚函数进行重写)**b.通过基类的指针或引用来调用虚函数。
静态多态:(先就函数重载来举例说明)如下代码:
int Add(int a, int b)
{
return a + b;
}
double Add(double a, double b)
{
return a + b;
}
int main()
{
cout << Add(1, 2) << endl;
cout << Add(1.25, 3.14) << endl;
system("pause");
return 0;
}
具体的结果显示如下:
动态多态:
使用vistual关键字修饰类的成员函数时,指出该函数为虚函数,派生类重新实现一定要在派生类中对基类的虚函数进行重写。
举例说明如下:
class Base
{
public:
virtual void test()//成员函数为虚函数
{
cout << "Base::test()" << endl;
}
};
class Derive :public Base//采用公有继承
{
public:
virtual void test()//在Derive中要对基类的虚函数进行重写。(返回值类型,函数名,参数列表都必须一样)
{
cout << "Derive::test()" << endl;
}
};
int main()
{
Base a;
a.test();
Derive b;
b.test();
system("pause");
return 0;
}
运行结果如下:
简单介绍协变:基类与派生类中函数类型改变。父类的虚函数返回类型是父类的引用或指针,子类的虚函数返回类型是子类的引用或指针,但是还构成重写。
动态多态的第二种形式:通过基类的指针或引用来调用虚函数。具体的例子如下:
class Base
{
public:
virtual void test()//成员函数为虚函数
{
cout << "Base::test()" << endl;
}
};
class Derive :public Base
{
public:
virtual void test()
{
cout << "Derive::test()" << endl;
}
};
void Funtest(Base &a)
{
a.test();
}
int main()
{
Base a;
Funtest(a);
Derive b;
Funtest(b);
system("pause");
return 0;
}
通过基类的指针调用虚函数:具体例子如下:
class Base
{
public:
virtual void test()//成员函数为虚函数
{
cout << "Base::test()" << endl;
}
};
class Derive :public Base
{
public:
virtual void test()
{
cout << "Derive::test()" << endl;
}
};
typedef void(*Func)();
int main()
{
Base a;
(*((Func*)(*(int *)&a)))();
system("pause");
return 0;
}
上面两个例子满足了动态绑定的两个条件,其中还涉及到了继承的赋值兼容规则,我们可以看出,经过编译后,系统对引用所指向的对象进行类型剖析,进行了相应的调用。
简单介绍一下纯虚函数:
在成员函数的形参列表后面写上=0,则成员函数称为纯虚函数,包含纯虚函数的类叫做抽象类(也叫做接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。举例如下:
class Base
{
public:
virtual void test() = 0;//成员函数为纯虚函数
int _a;
};
class Derive :public Base
{
public:
virtual void test()
{
cout << "Derive::test()" << endl;
}
virtual void test1()
{
cout << "Derive::test1()" << endl;
}
void test2()
{
cout << "Derive::test2()" << endl;
}
int _b;
};
抽象类:一个类中至少有一个纯虚函数,就称该类为抽象类。
(1)、抽象类中含有没有任何功能的纯虚函数,所以抽象类只能当做其他类的基类进行应用,不能实例化出对象。
(2)、抽象类不能做函数的参数类型和返回类型。
(3)、可以声明指向抽象类的指针或引用,该指针可以指向它的派生类形成多态。
(4)、纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。
继承体系同名成员函数的关系(即函数重载,重写与重定义的关系)
函数重载:函数重载发生在同一作用域,函数名相同,参数列表不同,返回值可以不同。
重写(覆盖):不在同一作用域(分别在基类和派生类),函数名相同,参数相同,返回值相同(协边例外)。基类函数必须有virtual关键字,访问修饰符可以不同。
重定义(隐藏):在不同作用域中(分别在基类和派生类),函数名相同,在基类和派生类中只要不构成重写就是重定义。
剖析系统对对象的类型解析
下面我们来剖析系统对对象的类型解析。先来剖析单继承对象模型。
具体代码为:
class Base
{
public:
virtual void test()//成员函数为虚函数
{
cout << "Base::test()" << endl;
}
int _a;
};
class Derive :public Base
{
public:
virtual void test()
{
cout << "Derive::test()" << endl;
}
int _b;
};
int main()
{
Base a;
a._a = 1;
Derive b;
b._b = 2;
b._a = 3;
system("pause");
return 0;
}
下面通过调试分析其内部内存:如下图所示
具体的如图所示:基类的对象模型为:
分析派生类的对象模型,若是给派生类内多加一个重写的虚函数和一个普通函数,模型会是怎样呢?
代码如下:
class Base
{
public:
virtual void test()//成员函数为虚函数
{
cout << "Base::test()" << endl;
}
int _a;
};
class Derive :public Base
{
public:
virtual void test()
{
cout << "Derive::test()" << endl;
}
virtual void test1()
{
cout << "Derive::test1()" << endl;
}
void test2()
{
cout << "Derive::test2()" << endl;
}
int _b;
};
int main()
{
cout << sizeof(Base) << endl;
cout << sizeof(Derive) << endl;
Base a;
a._a = 1;
Derive b;
b._b = 2;
b._a = 3;
system("pause");
return 0;
}
结果显示如图:
进入派生类的虚表中看看:
可以看出派生类的虚表中只有虚函数。
派生类中的虚表构造为:
(1)、基类中的虚表拷贝一份。
(2)、检测派生类中是否对基类中虚函数进行重写->用派生类中重写的虚函数来替换相同偏移量位置的基类虚函数
(3)、在虚表之后添加派生类自己的虚函数
说明普通函数并没有虚表,且派生类与基类不能使用同一张虚表,有几个类就有几张虚表。
多继承:多继承的相关知识在上篇博客中已经有所介绍,下面来看看多继承中虚表的形式
在多继承中,派生类继承几个基类就有几个虚表指针几个虚表,按继承的顺序排序(先继承的基类的虚表在上面,后继承在下面),然后把派生类自己新增加的虚函数加在第一张虚表的后面。具体的例子为:
class A
{
public:
virtual void Funtest1()
{
cout << "A::Funtest1()" << endl;
}
int _a;
};
class B
{
public:
virtual void Funtest2()
{
cout<< "B::Funtest2() "<< endl;
}
int _b;
};
class C :public A, public B
{
public:
virtual void Funtest1()
{
cout << "C::Funtest1()" << endl;
}
virtual void Funtest2()
{
cout << "C::Funtest2()" << endl;
}
virtual void Funtest3()
{
cout << "C::Funtest3()" << endl;
}
int _c;
};
int main()
{
A a;
B b;
C c;
c._c = 1;
c._a = 2;
c._b = 3;
return 0;
}
进入内存中可看到:
进入虚表中一探究竟,结果如下图。
则可知在多继承中,派生类继承几个基类就有几个虚表指针几个虚表,按继承的顺序排序(先继承的基类的虚表在上面,后继承在下面),然后把派生类自己新增加的虚函数加在第一张虚表的后面。
菱形继承:
菱形继承的基本形式为:
但一般为了避免二义性,则用虚继承,所以下面我们来讨论虚继承的菱形继承。
具体代码如下:
class A
{
public:
virtual void Funtest1()
{
cout << "A::Funtest1()" << endl;
}
virtual void Funtest2()
{
cout << "A::Funtest2()" << endl;
}
int _a;
};
class B: virtual public A
{
public:
virtual void Funtest1()
{
cout << "B::Funtest1()" << endl;
}
virtual void Funtest3()
{
cout << "B::Funtest2()" << endl;
}
int _b;
};
class C :virtual public A
{
public:
virtual void Funtest1()
{
cout << "C::Funtest1()" << endl;
}
virtual void Funtest4()
{
cout << "C::Funtest2()" << endl;
}
int _c;
};
class D :public B, public C
{
public:
virtual void Funtest1()
{
cout << "D::Funtest1()" << endl;
}
virtual void Funtest3()
{
cout << "D::Funtest3()" << endl;
}
virtual void Funtest4()
{
cout << "D::Funtest4()" << endl;
}
virtual void Funtest5()
{
cout << "D::Funtest5()" << endl;
}
int _d;
};
typedef void(*Func)();
void PrintVF(A&a)
{
Func*pFun = (Func*)(*(int*)&a);
while (*pFun)
{
(*pFun)();
pFun++;
}
}
void PrintVF(B&b)
{
Func*pFun = (Func*)(*(int*)&b);
while (*pFun)
{
(*pFun)();
pFun++;
}
}
void PrintVF(C&c)
{
Func*pFun = (Func*)(*(int*)&c);
while (*pFun)
{
(*pFun)();
pFun++;
}
}
int main()
{
A a;
B b;
C c;
D d;
d._a = 1;
d._b = 2;
d._c = 3;
d._d = 4;
A&pa = d;
B&pb = d;
C&pc = d;
PrintVF(pa);
PrintVF(pb);
PrintVF(pc);
cout << sizeof(A) << endl;
cout << sizeof(B) << endl;
cout << sizeof(C) << endl;
cout << sizeof(D) << endl;
system("pause");
return 0;
}
运行结果如下图:
那么对其内部的内存分析得出:
则我们可以看到在该菱形继承中先继承B类,再继承C类,即按继承的顺序排序(先继承的基类的虚表在上面,后继承在下面),然后把派生类B类将自己在类中的未重写的虚函数显示,C类将自己将自己在类中的未重写的虚函数显示。D类中新增加的虚函数加在第一张虚表的后面。