标签:类重写 动态绑定 根据 派生 got com 定义 子类 编译错误
多态一词最初来源于希腊语,意思是具有多种形式或形态的情形,当然这只是字面意思,它在C++语言中多态有着更广泛的含义。
这要先从对象的类型说起!对象的类型有两种:

实例:Derived1类和Derived2类继承Base类
class Base
{};
class Derived1 : public Base
{};
class Derived2 : public Base
{};
int main()
{
Derived1 pd1 = new Derived1; //pd1的静态类型为Derived1,动态类型为Derived1
Base *pb = pd1; //pb的静态类型为Base,动态类型现在为Derived1
Derived2 pd2 = new Derived2; //pd2的静态类型为Derived2,动态类型现在为Derived2
pb = pd2; //pb的静态类型为Base,动态类型现在为Derived2
return 0;
}
对象有静态类型,也有动态类型,这就是一种类型的多态。
多态有静态多态,也有动态多态。 静态多态,比如函数重载,能够在编译器确定应该调用哪个函数;动态多态,比如继承加虚函数的方式(与对象的动态类型紧密联系,后面详解),通过对象调用的虚函数是哪个是在运行时才能确定的。

实例:函数重载 ( 指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数 )
long long Add(int left, int right)
{
return left + right;
}
double Add(float left, float right)
{
return left + right;
}
int main()
{
cout<<Add(10, 20)<<endl; //语句一
cout<<Add(12.34f, 43.12f)<<endl; //语句二
return 0;
}
编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),即可推断出要调用哪个函数,如果有对应的函数就调用该函数,否则出现编译错误。
进入动态多态前,先看一个普通继承的例子:
class Person
{
public:
void GoToWashRoom()
{
cout<<"Person-->?"<<endl;
}
};
class Man : public Person
{
public:
void GoToWashRoom()
{
cout<<"Man-->Please Left"<<endl;
}
};
class Woman : public Person
{
public:
void GoToWashRoom()
{
cout<<"Woman-->Please Right"<<endl;
}
};
int main()
{
Person per, *pp;
Man man, *pm;
Woman woman, *pw;
pp = &per;
pm = &man;
pw = &woman;
//第一组 //这些都是毫无疑问的
per.GoToWashRoom(); //调用基类Person类的函数
pp->GoToWashRoom(); //调用基类Person类的函数
man.GoToWashRoom(); //调用派生类Man类的函数
pm->GoToWashRoom(); //调用派生类Man类的函数
woman.GoToWashRoom(); //调用派生类Woman类的函数
pw->GoToWashRoom(); //调用派生类Woman类的函数
//第二组
pp = &man;
pp->GoToWashRoom(); //调用基类Person类的函数
pp = &woman;
pp->GoToWashRoom(); //调用基类Person类的函数
return 0;
}
运行结果:

第一组毫无疑问,通过本类对象和本类对象的指针就是调用本类的函数;
第二组中先让基类指针指向子类对象,然后调用该函数,调用的是基类的,后让基类指针指向另一个子类对象,调用的是还是基类的函数。
这是因为p的类型是一个基类的指针类型,那么在p看来,它指向的就是一个基类对象,所以调用了基类函数。就像一个int型的指针,不论它指向哪,读出来的都是一个整型(在没有崩溃的前提下),即使将它指向一个float。
再来对比着看下一个例子。
实例:继承+虚函数
class Person
{
public:
virtual void GoToWashRoom() = 0;
};
class Man : public Person
{
public:
virtual void GoToWashRoom()
{
cout<<"Man-->Please Left"<<endl;
}
};
class Woman : public Person
{
public:
virtual void GoToWashRoom()
{
cout<<"Woman-->Please Right"<<endl;
}
};
int main()
{
for (int i = 0; i < 10; i++)
{
Person *p;
if (i&0x01)
p = new Man;
else
p = new Woman;
p->GoToWashRoom();
delete p;
sleep(1);
}
return 0;
}
运行结果:

就像上边这个例子所演示的那样,通过重写虚函数(不再是普通的成员函数,是虚函数!),实现了动态绑定,即在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。
使用virtual关键字修饰函数时,指明该函数为虚函数(在例子中为纯虚函数),派生类需要重新实现,编译器将实现动态绑定。在上边例子中,当指针p指向Man类的对象时,调用了Man类自己的函数,p指向Woman类对象时,调用了Woman类自己的函数。
【动态绑定条件】
总结
tips:协变:在C++中,只要原来的返回类型是基类类型的指针或引用,新的返回值类型是派生类的指针或引用,覆盖的方法就可以改变返回类型,这样的返回类型称为协变返回类型。
//协变,也可以构成重写(覆盖),但返回值是该类类型的指针或引用
class Base
{
virtual Base * FunTest()
{
//do something
}
};
class Derived : public Base
{
Derived * FunTest()
{
//do something
}
};
容易混淆的点:

标签:类重写 动态绑定 根据 派生 got com 定义 子类 编译错误
原文地址:http://www.cnblogs.com/zhanghu52030/p/8004523.html