知识储备:
重提切片行为:
1.子类对象赋值给父类对象
2.父类型的指针指向了子类对象
3.父类型的引用引用子类的对象
虚函数:类的成员函数前面加virtual关键字,则这个成员函数称为虚函数
虚函数重写:当在子类的定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写(也称覆盖)了父类的这个虚函数
构成多态需要满足的条件:
1.函数的形参是父类的指针或者引用
2.调用的Func必须是虚函数的重写
构成多态的函数的调用只与调用函数函数的对象有关,(父类对象还是子类对象)
常规的函数调用只与类型有关,(父类还是子类)
class Person { public: virtual void BuyTickets() { cout << " 全价" << endl; } protected: string _name; // 姓名 }; class Student : public Person { public: virtual void BuyTickets() { cout << "半价" << endl; } protected: int _num; //学号 }; //void Fun(Peron p) //不构成多态,与类型有关,这里都调用的是基类的函数 //void Fun(Person* p) //构成多态 void Fun(Person& p) //构成多态 // 不够多态的常规调用 -- 跟类型有关 // 构成多态 -- 跟对象有关 //构成多态的行为 // 1.父类指针和引用 // 2.调用的func必须是虚函数的重写 { p.BuyTickets(); } void Test() { Person p; Student s; Fun(p); Fun(s); }
总结:
1. 派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
2. 基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性
3. 只有类的非静态成员函数才能定义为虚函数, 静态成员函数不能定义为虚函数
4. 如果在类外定义虚函数,只能在声明函数时加virtual,类外定义函数时不能加virtual。
5. 构造函数不能为虚函数,虽然可以将operator=定义为虚函数,但是最好不要将operator=定义为虚函数,因为容易使用时容易引起混淆。
6. 不要在构造函数和析构函数里面调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会发生未定义的行为。
7. 最好把基类的析构函数声明为虚函数。(why?析构函数比较特殊,因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里是因为编译器做了特 殊处理)
8.虚表是所有类对象实例共用的
class A //把基类的析构函数最好声明为虚函数 { public : virtual ~A() { cout << "~A()" << endl; } }; class B :public A { public: ~B() { cout <<"~B()" << endl; } }; int main() { B b; A& ra = b; A* p = new B; delete p; // p->destructor() + free(p)----只析构了A没析构B,如果~B()中有释放内存的操作,就会造成内存泄漏 //解决:将父类的析构函数定义成虚函数 //这里先发生切片,A类型的指针p 指向了B对象, //当将父类的析构写成虚函数,在实际调用的时候,构成多态与类型指针的类型无关,直接调B的析构 system("pause"); return 0; }纯虚函数:在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。
class Person { public: virtual void Display() = 0; //纯虚函数 protected: string _name; }; class Teacher : public Person { virtual void Display() { cout << "Teacher" << endl; } protected: string _name; }; class Student : public Person { virtual void Display() { cout << "Student" << endl; } protected: string _name; }; int main() { //Person p; //出错-抽象类无法创建对象 Person* p = new Teacher; p->Display(); p = new Student; p->Display(); return 0; }