#include <iostream>
#include <string>
using namespace std;
class Person{
public:
int id_;
string name_;
//构造函数不能是虚函数
virtual ~Person(){//析构函数可以是虚函数
cout<<"delete Person"<<endl;
}
virtual void print() = 0;//纯虚函数,没有函数体
};
//class 派生类名 :继承方式 基类名;默认是私有继承
/*
不管什么继承,父类的private成员始终为父类私有
public继承:父类的public和protected成员在子类中访问属性不变
private继承:父类的public和protected成员在子类中变成private
protected继承:父类public和protected成员在子类中变成protected
*/
class Men : public Person{
public:
Men(int id, string name){
this->id_ = id;
this->name_ = name;
}
~Men(){
cout<<"delete Men"<<endl;
}
void print()//父类的方法是虚函数,子类重写的方法也自动成为虚函数
{
cout<<"Men:"<<id_<<"\t"<<name_<<"\n";
}
};
class Women : public Person{
public:
Women(int id, string name){
this->id_ = id;
this->name_ = name;
}
~Women(){
cout<<"delete Women"<<endl;
}
void print()
{
cout<<"Women:"<<id_<<"\t"<<name_<<"\n";
}
};
int main()
{
Men* men = new Men(1,"Peter");
Women* women = new Women(2,"Lily");
/*
多态的实质是子类是重写父类的方法,用虚函数实现
用指向父类的指针,调用虚函数,实际执行的是子类的方法
*/
Person* person = men;
person->print();
person = women;
person->print();
delete men;//delete子类的同时,也会把父类delete
delete women;
return 0;
}
执行结果:
Men:1 Peter
Women:2 Lily
delete Men
delete Person
delete Women
delete Person
继承机制中,子类访问父类成员方式
不管什么继承,父类的private成员始终为父类私有
public继承:父类的public和protected成员在子类中访问属性不变
private继承:父类的public和protected成员在子类中变成private
protected继承:父类public和protected成员在子类中变成protected
虚函数实现原理
多态简单来说就是:父类的指针(或者引用)指向其子类的实例,然后通过父类的指针(或者引用)调用实际子类的成员函数。
在每个包含有虚函数的类的对象的最前面(是指这个对象对象内存布局的最前面)都有一个称之为虚函数指针(vptr)的东西指向虚函数表(vtbl),这个虚函数表(这里仅讨论最简单的单一继承的情况,若果是多重继承,可能存在多个虚函数表)里面存放了这个类里面所有虚函数的指针,当我们要调用里面的函数时通过查找这个虚函数表来找到对应的虚函数,这就是虚函数的实现原理。
注意一点,如果基类已经插入了vptr, 则派生类将继承和重用该vptr。vptr必须随着对象类型的变化而不断地改变它的指向,以保证其值和当前对象的实际类型是一致的。这就是C++多态性实现的原理。
需要注意的几点,总结(基类有虚函数):
1、每一个类都有虚表。编译器会为每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有对象共享。类的每个虚成员占据虚函数表中的一行。
2、每个对象实例的最前面都有一个虚函数指针,指向虚函数表。
3、虚表可以继承,如果子类没有重写虚函数,那么子类虚表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现。如果基类3个虚函数,那么基类的虚表中就有三项(虚函数地址),派生类也会有虚表,至少有三项,如果重写了相应的虚函数,那么虚表中的地址就会改变,指向自身的虚函数实现。如果派生类有自己的虚函数,那么虚表中就会添加该项。
4、派生类的虚表中虚函数地址的排列顺序和基类的虚表中虚函数地址排列顺序相同。
5、虚函数是运行时多态,一定要通过对象来调用,有隐藏的this指针;而static函数是在类创建时就会生成,无需对象实例调用,没有隐藏的this指针,所以static函数不能用virtual修饰。
6. 构造函数不能是虚函数。 虚函数的调用需要虚函数表指针,而该指针存放在对象的内容空间中。若构造函数声明为虚函数,那么由于对象还未创建,还没有内存空间,更没有虚函数表地址用来调用虚函数。