通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生不同的状态
//多态 人 学生 不同类创建的对象调同一个函数得到不同的结果
class Person
{
public:
//虚函数
virtual void BuyTicket()
{
cout << "全价买票" << endl;
}
};
class Student : public Person
{
public:
virtual void BuyTicket()
{
cout << "半价买票" << endl;
}
};
void Func(Person& people)
{
people.BuyTicket();
}
void Test()
{
Person Mike;
Func(Mike);
Student John;
Func(John);
}
int main()
{
return 0;
}
派生类中有一个和基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名、参数列表完全相同),称子类的虚函数重写了基类的虚函数
class Person
{
public:
virtual void BuyTicket()//父类虚函数
{
cout << "全价买票" << endl;
}
};
class Student : public Person
{
public:
virtual void BuyTicket()//子类重写父类虚函数
{
cout << "半价买票" << endl;
}
};
注意:
在重写基类虚函数时,派生类的虚函数不加virtual也可以构成重写(只不过这种写法不是很规范,不建议这样使用)
1.协变:基类与派生类虚函数返回值类型不同
虚函数返回值类型可以不同,但是必须是父子类关系的指针或者引用
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;
}
};
2.析构函数的重写 :基类与派生类析构函数的名字不同
class Person
{
public:
virtual ~Person()
{
cout << "Person()" << endl;
}
};
class Student:public Person
{
public:
virtual ~Student()
{
cout << "Student()" << endl;
}
};
int main()
{
Person* p1 = new Person;
delete p1;
Person* p2 = new Student;
delete p2;
return 0;
}
重载
两个函数在同一作用域
函数名参数相同
重写(覆盖)
两个函数分别在基类和派生类的作用域
函数名/参数/返回值类型必须都相同(协变除外)
两个函数必须是虚函数
重定义(隐藏)
两个函数必须在基类和派生类的作用域
函数名相同
两个基类和派生类同名函数不构成重写就是重定义
C++11增加了override和final
1. final:修饰虚函数,表示该虚函数不能再被重写
2.override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。
class car
{
public:
virtual void Drive() = 0;
};
class Benz :public car
{
public:
virtual void Drive()
{
cout << "Benz" << endl;
}
};
int main()
{
car* pBenz = new Benz;
pBenz->Drive();
return 0;
}
class Base
{
public:
virtual void Func1()
{
cout << "Base::Func1()" << endl;
}
virtual void Func2()
{
cout << "Base::Func2()" << endl;
}
private:
int _b = 10;
};
int main()
{
Base b;
cout<
先计算一下sizeof(b)是多少?
明明只有一个成员变量_b,为什么计算的结果是8呢?
有了虚函数后对象里面会多一个指针,叫虚函数表指针
增加一个派生类Derive去继承Base
class Base
{
public:
virtual void Func1()
{
cout << "Base::Func1()" << endl;
}
virtual void Func2()
{
cout << "Base::Func2()" << endl;
}
void Func3()
{
cout << "Base::Func3()" << endl;
}
private:
int _b = 10;
};
class Drive :public Base
{
public:
virtual void Func1()
{
cout << "Drive::Func1()" << endl;
}
private:
int _d = 0;
};
先将基类中的虚表内容拷贝一份到派生类虚表中如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。
多个虚函数在虚表中谁先谁后呢?先声明的在前面虚函数存在哪的?虚表存在哪的?虚函数和普通函数一样的,都是存在代码段的, 只是 它的指针又存到了虚表中。
虚表存的是虚函数指针 (满足多态调用的时候,运行时到指向的对象中找到对应的虚函数)。另外对象中存的不是虚表,存的是虚表指针。实际我们去验证一下会发现vs下虚表是存在代码段的
class Base
{
public:
virtual void Func1()
{
cout << "Base::Func1()" << endl;
}
virtual void Func2()
{
cout << "Base::Func2()" << endl;
}
void Func3()
{
cout << "Base::Func3()" << endl;
}
private:
int _b = 10;
};
class Drive :public Base
{
public:
virtual void Func1()
{
cout << "Drive::Func1()" << endl;
}
private:
int _d = 0;
};
int main()
{
Base b1;
Drive d1;
int i = 0;
static int j = 1;
int* p = new int;
const char* p1 = "sdhajsd";
printf("栈:%p\n", &i);
printf("静态区:%p\n", &j);
printf("堆:%p\n", p);
printf("常量区:%p\n",p1);
Base* p2 = &b1;
Drive* p3 = &d1;
printf("Base:%p\n",*(int*)p2);
printf("Drive:%p\n",*(int*)p3);
return 0;
}
看着是常量区的
看一下下面的代码思考一个问题
怎么做到指向父类的调用父类,指向子类的调用子类?
class Base
{
public:
virtual void Func1()
{
cout << "Base::Func1()" << endl;
}
virtual void Func2()
{
cout << "Base::Func2()" << endl;
}
void Func3()
{
cout << "Base::Func3()" << endl;
}
private:
int _b = 10;
};
class Drive :public Base
{
public:
virtual void Func1()
{
cout << "Drive::Func1()" << endl;
}
private:
int _d = 0;
};
void f(Base* ptr)
{
ptr->Func1();
}
int main()
{
Base b;
Drive d;
f(&b);
f(&d);
return 0;
}
看一下以下代码
class Base
{
public:
virtual void Func1()
{
cout << "Base::Func1()" << endl;
}
virtual void Func2()
{
cout << "Base::Func2()" << endl;
}
void Func3()
{
cout << "Base::Func3()" << endl;
}
private:
int _b = 10;
};
class Drive :public Base
{
public:
virtual void Func1()
{
cout << "Drive::Func1()" << endl;
}
private:
int _d = 0;
};
//运行时决议和编译时决议
int main()
{
Base b;
Drive d;
Base* p = &b;
p->Func1();
p->Func3();
p = &d;
p->Func1();
p->Func3();
return 0;
}
这里 Func3 为什么不是 Derive 的?因为 Func3 不是虚函数,它没有进入虚表。
如果从更深的角度 —— 汇编层面去看,就可以牵扯出编译时决议和运行时决议。
上代码
class Base
{
public:
virtual void Func1()
{
cout << "Base::Func1()" << endl;
}
virtual void Func2()
{
cout << "Base::Func2()" << endl;
}
void Func3()
{
cout << "Base::Func3()" << endl;
}
private:
int _b = 10;
};
class Drive :public Base
{
public:
virtual void Func1()
{
cout << "Drive::Func1()" << endl;
}
virtual void Func3()
{
cout << "Drive::Func3()" << endl;
}
virtual void Func4()
{
cout << "Drive::Func4()" << endl;
}
private:
int _d = 0;
};
int main()
{
Base b;
Drive d;
return 0;
}
看不到Func3和Func4
class Base
{
public:
virtual void Func1()
{
cout << "Base::Func1()" << endl;
}
virtual void Func2()
{
cout << "Base::Func2()" << endl;
}
void Func3()
{
cout << "Base::Func3()" << endl;
}
private:
int _b = 10;
};
class Drive :public Base
{
public:
virtual void Func1()
{
cout << "Drive::Func1()" << endl;
}
virtual void Func3()
{
cout << "Drive::Func3()" << endl;
}
virtual void Func4()
{
cout << "Drive::Func4()" << endl;
}
private:
int _d = 0;
};
typedef void(*VF_PTR) ();
void PrintVTable(VF_PTR vft[])
{
//打印虚表本质是打印指针(虚函数指针)数组
//数组中的虚函数指针打印并调用。调用就可以看出存的是哪个函数
cout << " 虚表地址>" << vft<< endl;
for (int i = 0; vft[i] != nullptr; ++i)
{
printf("[%d]:%p->", i, vft[i]);
VF_PTR f = vft[i];//函数指针去接收地址
f();//通过这个地址去调函数
}
cout << endl;
}
int main()
{
Base b;
Drive d;
VF_PTR * vft1 = (VF_PTR*)(*(int*)&b);
PrintVTable(vft1);
VF_PTR* vft2 = (VF_PTR*)(*(int*)&d);
PrintVTable(vft2);
return 0;
}
class Base1 {
public:
virtual void func1()
{
cout << "Base1::func1" << endl;
}
virtual void func2()
{
cout << "Base1::func2" << endl;
}
private:
int b1;
};
class Base2 {
public:
virtual void func1()
{
cout << "Base2::func1" << endl;
}
virtual void func2()
{
cout << "Base2::func2" << endl;
}
private:
int b2;
};
class Derive : public Base1, public Base2 {
public:
virtual void func1()
{
cout << "Derive::func1" << endl;
}
virtual void func3()
{
cout << "Derive::func3" << endl;
}
private:
int d1;
};
typedef void(*VF_PTR) ();
void PrintVTable(VF_PTR vft[])
{
//打印虚表本质是打印指针(虚函数指针)数组
//数组中的虚函数指针打印并调用。调用就可以看出存的是哪个函数
cout << " 虚表地址>" << vft<< endl;
for (int i = 0; vft[i] != nullptr; ++i)
{
printf("[%d]:%p->", i, vft[i]);
VF_PTR f = vft[i];//函数指针去接收地址
f();//通过这个地址去调函数
}
cout << endl;
}
int main()
{
Derive d;
cout << sizeof(d) << endl;
Base1* ptr1 = &d;
Base2* ptr2 = &d;
PrintVTable((VF_PTR*)(*(int*)ptr1));
PrintVTable((VF_PTR*)(*(int*)ptr2));
return 0;
}
可以看到d中明显有两张虚表
观察下图可以看出:多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中