多态是:
- 同一个方法在基类和派生类中的行为不同,方法的行为取决于调用方法的对象。
- 实现方式:
1.在派生类中重新定义基类需要实现多态的方法
2.使用虚方法(虚拟成员函数,在方法声明前加上virtual关键字)。编译器对虚方法采用 动态联编(将函数调用推迟到运行时决定) - 动态联编的原因是:继承关系中的第二条。基类指针或引用可以指向派生类对象所导致的。
class A
{
public:
void funPrint(){cout<<"funPrint of class A"<<endl;};
};
class B:public A
{
public:
void funPrint(){cout<<"funPrint of class B"<<endl;};
};
void main()
{
A *p; //定义基类的指针
A a;
B b;
p=&a;
p->funPrint();
p=&b;
p->funPrint();
}
大家以为这段代码的输出结果是什么?有的人可能会马上回答funPrint of class A 与 funPrint of class B 因为第一次输出是引用类A的实 例啊,第二次输出是引用类B的实例啊。那么我告诉你这样想就错啦,答案是funPrintof class A 与 funPrint of class A。因为p是一个A类的指针,所以不管你将p指针指向类A或是类B,最终调用的函数都是类A的funPrint 函数。这就是静态联篇,编译器在编译的时候就已经确定好了。可是如果我想实现跟据实例的不同来动态决定调用哪个函数呢?这就须要用到 虚函数(也就是动态联篇)
虚函数(多态概念的c++实现)
class A
{
public:
virtual void funPrint(){cout<<"funPrint of classA"<<endl;};//虚函数
};
class B:public A
{
public:
virtual void funPrint(){cout<<"funPrint of classB"<<endl;};
};
void main()
{
A *p; //定义基类的指针
A a;
B b;
p=&a;
p->funPrint();
p=&b;
p->funPrint();
}
此时由于继承的A中调用函数是虚函数,编译器采用动态联编,结果就会出现多态性。同一个操作
(p->funprint),不同的结果。
虚基类
为了解决菱形继承体系下,类d,通过类b,类c两条途径获得类a数据的两份拷贝。和可能的命名冲突。通过让类b与类c使用虚拟继承类a来声明类a是共享的。来消除歧义
//间接基类A
class A{
protected:
int m_a;
};
//直接基类B
class B: virtual public A{ //虚继承,则类A叫做虚基类
protected:
int m_b;
};
//直接基类C
class C: virtual public A{ //虚继承
protected:
int m_c;
};
//派生类D
class D: public B, public C{
public:
void seta(int a){ m_a = a; } //正确,如果类b,c没有声明为虚继承,则此处命名冲突
void setb(int b){ m_b = b; } //正确
void setc(int c){ m_c = c; } //正确
void setd(int d){ m_d = d; } //正确
private:
int m_d;
};
int main(){
D d;
return 0;
}
抽象类(纯虚函数)
类中含有纯虚函数的类,纯虚函数只是一个接口,没有实现(因此,抽象类无法被实例化)。
class Vehicle
{
public:
virtual void PrintTyre()=0; //纯虚函数是这样定义的.该类为抽象类
};
class Camion:public Vehicle //继承抽象类,要实现接口,如果不实现,则该类也是抽象类
{
public:
virtual void PrintTyre(){cout<<"Camion tyrefour"<<endl;};//virtual关键字不是必需的
};
class Bike:public Vehicle
{
public:
virtual void PrintTyre(){cout<<"Bike tyre two"<<endl;};
};
void main()
{
Camion c;
Bike b;
b.PrintTyre();
c.PrintTyre();
}