多态调用和普通调用
C++中只有两个调用关系,分为普通调用和多态调用。不满足多态调用就是普通调用。
多态必须构成的两个条件
- 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写(也就是必须满足重写)
- 必须通过基类的指针或者引用调用虚函数(指针和引用)
多态调用:跟指向的对象有关,传的对象是谁的,调用的就是谁的。
普通调用:和类型有关。类型是什么调用就是什么,类型是Person的,那么调用的函数就是Person的。 比如下面的例子。
普通调用的例子:
class A {};
class B : public A{};
class Person{
public:
virtual B*f(){
cout << "Person::f()" << endl;
return new B;
}
};
class Student: public Person{
public:
virtual B* f(){
cout << "Student::f()" << endl;
return new B;
}
};
int main ()
{
Person p;
p.f();
Student s;
p = s; //派生类赋值给子类
p.f();
system("pause");
}
结果:
分析:
在这里虽然调用的是虚函数,派生类中也对基类的虚函数进行了重写。但是不是使用基类的指针或引用调用,所以不满足多态调用的条件。
所以在这里是普通调用。只与类型有关,尽管Student对象s赋值给父类对象p,但是普通调用只与类型有关,所以仍然调用p的f()函数
多态调用的例子
class A {};
class B : public A{};
class Person{
public:
virtual B*f(){
cout << "Person::f()" << endl;
return new B;
}
};
class Student: public Person{
public:
virtual B* f(){
cout << "Student::f()" << endl;
return new B;
}
};
int main()
{
Person p;
Student s;
Person* ptr = &p;
//本来就是Person类的指针,指向Person类的对象,调用的本来就是Person父类的f()函数
ptr->f() ;
ptr = &s;
ptr->f(); //指向的是子类的对象, 调用的是子类的f()的函数
}
结果:
分析:
在这里被调用的是虚函数,且派生类Student里对虚函数f()进行了重写。又是基类的指针对虚函数的调用,所以满足多态调用。
ptr是基类的指针,指向的是派生类的对象, 基类的指针调用虚函数,构成多态调用,只和对象有关。所以调用的是派生类的f()函数。
错误多态调用的例子
值得注意的一点: 必须是基类的指针对虚函数的调用,下面有一个派生类指针对虚函数的调用
class A {};
class B : public A{};
class Person{
public:
virtual B*f(){
cout << "Person::f()" << endl;
return new B;
}
};
class Student : public Person{
public:
virtual B* f(){
cout << "Student::f()" << endl;
return new B;
}
};
int main()
{
Student s;
// 派生类指针指向派生类的对象,调用虚构函数,肯定是调用自己的,因为自己完成了对虚函数的重写
Student* ps = &s;
ps->f();
cout << endl;
Person p;
Person* ptr = &p; // Person类的指针, 指向Person类的对象,调用的本来就是Person类的虚函数
ptr->f();
cout << endl;
ps = (Student*)ptr; //将基类Person的指针强转 赋给派生类Student的指针ps
ps->f(); //派生类的指针调用虚函数, 所以不构成重写, 不满足多态调用。 所以和对象没有关系,只与类型有关系
system("pause");
}
结果:
分析:
在这里看最后一个输出的Person::f()。
将基类Person的指针强转赋给派生类Student的指针ps。
派生类的指针调用虚函数, 所以不构成重写, 不满足多态调用。 所以和对象没有关系,只与类型有关。 在这里ptr是Person * 类型的,是强转成Student * 类型的, 所以输出的只与类型有关, 输出Person * 的f(). 所以输出的是Person->f()