函数多态就相当于函数的重载。
在调用函数时,一个函数名可以接受不同的参数列表,执行不同的代码,所以具有多态性。
动态多态:
程序运行时的多态,主要是就 对象 而言的。但C++中实现多态不能直接用对象,而是用指向对象的指针或引用。
将基类指针转换为派生类指针就是向下转换,如:
Drerived *D=static_cast<Base*>(*B);
当基类中的函数不能满足派生类的特殊性,就可以考虑使用动态多态。
如何实现动态多态:
1.与多态行为相关的函数,在基类中声明为虚函数。
2.派生类改写基类中的虚函数。
3.通过基类类型的指针(或引用)调用虚函数。
派生类重新声明虚函数时:
1.函数名相同
2.参数列表相同
3.与基类中的const或非const成员函数一致
4.返回值类型相同,或者是存在继承关系类型的指针,即派生类虚函数返回指针的类型继承自基类返回指针的类型。
举例,B,C是正确的虚函数声明。
A.Base*Base::copy(Base*)
Base*Derived::copy(Derived*)//不满足2
B.Base*Base::copy(Base*)
Derived*Derived::copy(Base*)
C.ostream& Base::print(int,ostream&=cout)//调用默认实参是由指针类型决定的。
ostream& Derived::print(int,ostream&)
D.void Base::eval()const
void Derived ::eval()//不满足3
派生类重新声明虚函数时,virtual关键字可加可不加。一个成员函数只要在基类里声明为虚函数,则在其后所有层次的派生类中都是虚函数。
如果基类指针调用一个非虚函数,而该函数又调用了一个虚函数。则实际被调用的也是基类指针所指对象的虚函数。指向子类对象就调用的是子类对象的虚函数。
当使用类的指针调用成员函数时,普通函数由指针类型决定,而虚函数由指针指向的实际类型决定 虚函数调用实例
纯虚函数
virtual <返回类型> function(<参数列表>)=0;
纯虚函数首先是虚函数。如果某个虚函数不需要在基类中实现,或者实现也没有实际意义,则应该声明为纯虚函数。
有纯虚函数的类是一个抽象类,是不能实例化对象的。
如:
class Shape{
shape(){}
~shape(){}
virtual void Draw()=0;
};
int main(){
Shape s;//错误,Shape是一个抽象类,是不能实例化对象的。
return 0
}
派生类必须改写基类中所有的纯虚函数,否则还是一个抽象类。
虚函数表
一个类中,如果定义了虚函数,那就会分配一个虚函数表和虚函数表指针。发生继承时,派生类也会创建虚函数表。
虚析构函数
基类指针指向派生类对象,普通析构只会调用基类析构函数。如果基类声明了虚析构函数,那析构的时候就会从派生类开始调用析构函数,直到基类的析构函数。
举例:
#include<iostream>
#include<cstdlib>
#include<vector>
using namespace std;
struct Base{
virtual void move() = 0;//纯虚函数。
virtual ~Base(){
cout << "delete" << endl;
}//虚析构函数
};
class Truck :public Base{
public:
/*virtual*/void move(){//纯虚函数重写
cout << "Truck move" << endl;
}
};
class Boat :public Base{
void move(){
cout << "Boat move" << endl;
}
};
class Plane :public Base{
void move(){
cout << "Plane move" << endl;
}
};
int main(){
vector<Base*>base;//创建指向基类的指针数组
base.push_back(new Truck());
base.push_back(new Boat());
base.push_back(new Plane());
vector<Base*>::iterator iter = base.begin();
while (iter != base.end()){
(*iter)->move();
iter++;
}
iter = base.begin();
while (iter != base.end()){
delete (*iter);
iter++;
}
system("pause");
return 0;
}
输出:
Truck move
Boat move
Plane move
delete
delete
delete
虚函数的sizeof问题:
包含虚函数的类中,是有一个指向虚函数表的指针。
sizeof(虚函数)=sizeof(虚函数指针)=4;
虚继承
参考:
虚拟继承与sizeof
虚继承的时候,是有一个指向父类的虚类指针。
普通继承就是直接把父类的sizeof大小+子类新添的内容。
虚继承还有加一个指向父类的虚类指针的大小。
不同的编译器虚继承时,虚表指针共享方法是不一样的。
GCC不管是不是虚继承,虚表指针都是共享的;
VC虚继承时,虚表函数是不共享的。非虚继承时,虚表指针是共享的(就是自己类里也有虚函数,算不算自己这个虚函数指针的问题)
如果虚继承的时候,虚函数覆盖了。那虚表指针只能算一个。
#include<iostream>
#include<assert.h>
#include<memory.h>
#include<vector>
using namespace std;
class A{
char k[3];
public:
virtual void aa(){};
};
class B:public virtual A{
char j[3];
public:
virtual void aa(){};//virtual void bb(){};如果换成bb(),则输出 8,20,32
};
class C :public virtual B{
char i[3];
public:
virtual void cc(){};
};
int main(){
cout << "sizeof(a)" << sizeof(A) << endl;//8
cout << "sizeof(b)" << sizeof(B) << endl;//16
cout << "sizeof(C)" << sizeof(C) << endl;//28
system("pause");
return 0;
}
还有一种是两个类没有继承关系的
具体问题详见:C++多态的一个例子
- 其中,
A* p = (A*)&object;
它主要就是为了能让A类的指针指向object的地址,因为A和B类不是继承关系,所以如果不加(A*)是不能指过去的,VS里会报错。 - p->f2() 为什么输出的是 B::f1
找到f2函数在A的虚函数表中的位置,
然后找到B里这个位置上的虚函数。