多态即多种形态,对于C++程序设计中指的是在类的实例化对象中,当不同的对象去完成某个行为时会出现不同的状态。
虚函数:即被virtual修饰的类成员函数称为虚函数。如下示例:
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl;}
};
派生类中有一个跟基类完全相同的虚函数,即子类中满足三同(函数名、参数、返回值)相同的虚函数,叫做重写(覆盖)
在派生类中虚函数函数重写构成多态的示例及注意事项如下:
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
/*注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时
,虽然也可以构成重写(因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性)
,但是该种写法不是很规范,不建议这样使用*/
/*void BuyTicket() { cout << "买票-半价" << endl; }*/
virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
//1、构成多态,跟p的类型没有关系,传的哪个类型的对象,
//调用的就是这个类型的虚函数 -- 跟对象有关
//2、不构成多态,调用就是p类型的函数 -- 跟类型有关
void Func(Person& p)
{ p.BuyTicket(); }
int main()
{
Person ps;
Student st;
Func(ps);
Func(st);
return 0;
}
派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
class A{};
class B : public A {};
class Person {
public:
virtual A* f() {return new A;}
};
class Student : public Person {
public:
virtual B* f() {return new B;}
};
如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。
class Person {
public:
virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:
virtual ~Student() { cout << "~Student()" << endl; }
};
//只有派生类Student的析构函数重写了Person的析构函数,
//下面的delete对象调用析构函数,才能构成多态,
//才能保证p1和p2指向的对象正确的调用析构函数。
int main()
{
// 普通对象,析构函数是否虚函数,是否完成重写,都正确调用了
// Person p;
// Student s;
// 动态申请的对象,如果给了父类指针管理,那么需要析构函数是虚函数
Person* p1 = new Person; // operator new + 构造函数
Person* p2 = new Student;
// 虚函数的重写允许,两个都是虚函数或者父类是虚函数,再满足三同,就构成重写。
// 其实这个是C++不是很规范的地方,当然我们建议两个都写上virtual
// 本质上,子类重写的虚函数,可以不加virtual是因为析构函数,设计初衷
// 父类析构函数加上virtual,那么就不存在不构成多态,没调用子类析构函数,内存泄漏场景
// 建议,我们自己写的时候,都加上virtual,肯定没毛病
// 析构函数 + operator delete
// p1->destructor()
delete p1;
delete p2;
// p2->destructor()
return 0;
}