多态
去完成某个行为时,不同的对象会产生出不同的状态。
多态的定义
多态是不同继承关系的类对象,去调用同一函数,产生不同的行为。
构成多态的条件:
- 调用函数的对象必须是指针或者引用;
- 被调用的函数必须是虚函数,且完成了虚函数的重写。
虚函数
在类的成员函数前面加virtual关键字。
class Person
{
public:
virtual void test()
{
cout << "test" << endl;
}
};
虚函数重写
派生类中有一个跟基类完全相同的虚函数,称为子类的虚函数重写了基类的虚函数。完全相同指:函数名,参数,返回值都相同,虚函数的重写也叫虚函数覆盖。
class Person
{
public:
virtual void BuyTicket()
{
cout << "全价"<<endl;
}
};
class Student:public Person
{
public:
virtual void BuyTicket()
{
cout << "半价" << endl;
}
};
void Func(Person& p)
{
p.BuyTicket();
}
int main()
{
Person pe;
Student st;
Func(pe);
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 << "~Prson" <<endl;
}
};
class Student:public Person
{
pubilc:
virtual ~Student()
{
cout << "~Student " << endl;
}
};
int main()
{
Person *p1 = new Person;
Person *p2 = new Student;
delete p1;
delete p2;
return 0;
}
接口继承与实现继承
普通函数继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。
虚函数的继承是一种接口继承,派生类继承的是基类函数的接口,目的是为了重写,达成多态,继承的是接口,所以若不实现多态,不要把函数函数定义成虚函数。
重载、重写、隐藏的区别
重载:两个函数在同一个作用域,函数名/参数相同。
重写:两个函数分别在基类和派生类的作用域;函数名/参数/返回值都必须相同;两个函数必须是虚函数。
隐藏:两个函数分别在基类和派生类的作用域;函数名相同;两个基类和派生类的同名函数不构成重写就是隐藏。
抽象类
在虚函数后写上=0,则这个函数为纯虚函数,包含纯虚函数的类叫抽象类(也叫接口类),抽象类不能实例化对象。派生类继承后也不能实例化对象,只有重写纯虚函数,派生类才能实例化出对象。
class Car
{
public:
virtual void Drive()=0;
};
class Benz:public Car
{
public:
virtual void Drive()
{
cout << "舒适 " << endl;
}
};
class BWM:public Car
{
public:
virtual void Drive()
{
cout << "宽敞" <<endl;
}
};
void test()
{
Car *pBenz = new Banz;
pBanz->Drive();
Car* pBWM = new BWM;
pBWM->Drive();
}