一、多态的条件
- 虚函数重写
- 父类指针或者引用去调用
普通调用,调用函数的是类型是谁,就调的这个类型的函数
多态调用,调用指针或者引用指向引用指向的对象,指向父类调用父类的函数,指向子类调用子类的函数
1.2、破坏多态的条件的情形
1.2.1缺少继承关系
class Person {
public:
virtual Person* BuyTicket()
{
cout << "Person买票,全价" << endl;
return nullptr;
}
};
class Student {
public:
Student* BuyTicket()
{
cout << "Student买票,半价" << endl;
return nullptr;
}
//void BuyTicket() { cout << "买票,半价" << endl; }
};
void Func(Person& p)
{
p.BuyTicket();
}
int main()
{
Person ps;
Student st;
Func(ps);
Func(st);
return 0;
}
情形:编译报错,Func不能传Student上去。
1.2.2既不是引用也不是指针
class Person {
public:
virtual Person* BuyTicket()
{
cout << "Person买票,全价" << endl;
return nullptr;
}
};
class Student :public Person{
public:
Student* BuyTicket()
{
cout << "Student买票,半价" << endl;
return nullptr;
}
//void BuyTicket() { cout << "买票,半价" << endl; }
};
void Func(Person p)
{
p.BuyTicket();
}
int main()
{
Person ps;
Student st;
Func(ps);
Func(st);
return 0;
}
1.2.3、虚函数的重写
要求都是虚函数,并且三同(协变例外)
class Person {
public:
void BuyTicket()
{
cout << "Person买票,全价" << endl;
return nullptr;
}
};
class Student :public Person{
public:
virtual void BuyTicket()
{
cout << "Student买票,半价" << endl;
}
};
不构成多态。
class Person {
public:
virtual void BuyTicket()
{
cout << "Person买票,全价" << endl;
}
};
class Student :public Person{
public:
//虚函数
void BuyTicket()
{
cout << "Student买票,半价" << endl;
}
};
void Func(Person p)
{
p.BuyTicket();
}
int main()
{
Person ps;
Student st;
Func(ps);
Func(st);
return 0;
}
构成多态。因为Student构成了虚函数的重写。
练习
class A
{
public:
virtual void func(int val = 1) { cout << "A->" << val << endl; }
virtual void test() { func(); }
};
class B :public A
{
public:
void func(int val = 0) { cout << "B->" << val << endl; }
};
int main()
{
B* p = new B;
p->test();
return 0;
}
运行结果():
A、A->0 B、B->1 C、A->0 D、B->0 E、编译出错 F、以上都不正确
分析:
- p->test()是A*去调用test(),(继承不是将父类的函数都在子类拷贝一份,而只是可以用)
- this->func()是多态调用:func()满足三同原则,父类有virtual,所以满足多态条件,所以调用子类。
- 多态时使用的是父类的接口,所以value的缺省值为1
答案:B
二、重载、覆盖(重写)、隐藏(重定义)的对比
三、 C++11 override和final
3.1 final:修饰虚函数,表示该虚函数不能再被重写
class Car
{
public:
virtual void Drive() final{}
};
class Benz :public Car
{
public:
virtual void Drive() { cout << "Benz-舒适" << endl; }
};
final修饰类,不能被继承,修饰虚函数,不能被重写,不是虚函数,不能用fianl修饰。
3.2 override:检查是否完成重写
class Car {
public:
virtual void Drive(){}
};
class Benz :public Car {
public:
//override修饰派生类的虚函数
//检查是否完成重写
virtual void Drive() override { cout << "Benz-舒适" << endl; }
};
四、抽象类
3.1概念
在虚函数的后面协商=0,则这个函数为纯虚函数。**包含纯虚函数的类的叫做抽象类(也叫接口类),抽象了不能实例化对象。**派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现了接口继承。
class Car {
public:
virtual void Drive() = 0;
};
int main()
{
Car c;
Car* s;
return 0;
}
这里的Car就是一个抽象类。但这里的指针s可以。(C++规定)
class Car {
public:
virtual void Drive() = 0;
};
class Benz :public Car
{
public:
virtual void Drive()
{
cout << "Benz——舒适" << endl;
}
};
int main()
{
Benz b;
b.Drive();
return 0;
}
可以看到,只有重写了Drive(),才能正确运行。
class Car {
public:
virtual void Drive() = 0;
};
class Benz :public Car
{
public:
virtual void Drive()
{
cout << "Benz——舒适" << endl;
}
};
class BMW :public Car
{
public:
virtual void Drive()
{
cout << "BMW——舒适" << endl;
}
};
void func(Car* ptr)
{
ptr->Drive();
}
int main()
{
func(new Benz);
func(new BMW);
return 0;
}
纯虚函数间接强制了派生类去重写,抽象类——不能实例化出对象
五、原理
存在一个指针vfptr,C++会把虚函数存到一个虚函数表的地方去,所以按内存对齐的原则,其大小为12。一个含有虚函数的类中至少都有一个虚函数表指针,因为虚函数的地址需要放到虚函数表中,虚函数也简称为虚表。
六、多态的原理(重点)
class Person
{
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
virtual void func(){}
private:
int a = 0;
};
class Student :public Person {
public:
virtual void BuyTicket() { cout << "买票-半价" << endl; }
private:
int b = 1;
};
void Func(Person& p)
{
p.BuyTicket();
}
int main()
{
Person Mike;
Func(Mike);
Student Johnson;
Func(Johnson);
return 0;
}