虚函数:给成员函数前面加上virtual关键字。
1.派生类中定义虚函数必须与基类中的虚函数同名外,还必须同参数表,同返回类型。否则被认为是重载,而不是重写。如基类中返回基类指针,派生类中返回派生类指针是允许的,这是一个例外(协变)。
2.只有类的成员函数才能说明为虚函数。这是因为虚函数仅适用于有继承关系的类对象。友元函数和全局函数不能作为虚函数。
3.静态成员函数,是所有同一类对象共有,不受限于某个对象,不能作为虚函数。
4.内联函数每个对象一个拷贝,无映射关系,不能作为虚函数。
5.构造函数(拷贝构造,移动构造)不能作为虚函数。因为构造函数的另一个作用是设置虚表指针。
6.析构函数可定义为虚函数,在基类中及其派生类中都获取系统资源时时,必须把析构函数定义为虚函数,实现撤消对象时的多态性。
7.实现动态多态性时,必须使用基类类型的指针变量或引用,使该指针指向该基类的不同派生类的对象,并通过该指针指向虚函数,才能实现动态的多态性。
8.虚函数执行速度要稍慢一些,为了实现多态性,每一个派生类中均要保存相应虚函数的入口地址表,函数的调用机制也是间接实现。所以多态性总是要付出一定代价,但通用性是一个更高的目标。
9.如果虚函数定义放在类外,virtual只能加在函数声明前面,不能再加在函数定义前面。
10.如果不使用继承和多态,那么就把函数中的virtual关键字去掉,加上的话virtual也没必要,还会增大程序的开销。
纯虚函数:没有具体实现的虚成员函数
定义纯虚函数的一般格式为:
virtual 返回类型 函数名(参数表) = 0;
“=0"表明程序员将不定义该函数,函数声明是为派生类保留一个位置。”=0"本质上是将指向函数体的指针定为NULL(不同编译器的处理不同)。
告诉编译器我们不实现这个函数,由派生类来实现。
含义纯虚函数的类是抽象类
1.抽象类不能实例化对象
2.派生类继承了抽象类,则必须把抽象类中的纯虚函数实现了,否则也不能创建对象
3.在程序设计时,抽象类一般都做为基类来使用
当基类继承抽象类时,必须把抽象类中的所有纯虚函数进行重写,如果有一个没有重写,那么基类就是抽象类,全部实现之后,就称为(实现类)
4.抽象类不能做参数类型,函数返回值类型或显示类型转换,抽象类型可以定义指针和引用(可以指向它不同的派生对象,以实现运行时多态)
应用类型:不提供派生也不提供继承
节点类型:提供继承和多态的继承(有虚函数),但没有纯虚函数
抽象类型:抽象类,只能用作基类
接口类:没有属性,所有函数都是纯抽象函数
实现类:继承了接口或者抽象类,实现了纯虚函数
纯虚函数没实现时,对应的该函数指向nullpter(不同的环境实现不同),当创建对象时,进行查虚表,当它的函数为nullpter时,则会报错(纯虚函数会在虚表中保留一个位置,让派生类实现虚表时,把它对该函数的具体实现的地址填充进去它)
visaul stdio的实现:它则把这些函数指向一个函数(默认的缺省的纯虚函数,调用时会抛出异常)
class Base
{
public:
virtual void fun() = 0;
virtual ~Base(){}
};
class Child :public Base
{
public:
virtual void fun()const {}
virtual ~Child(){}
};
Child 可以创建对象吗?
不能,因为这是同名隐藏(两个函数的参数列表不同),不属于同名覆盖。去掉const则就可以了
类中全部都是成员方法,没有属性,而且成员方法全部都是纯虚函数
申明纯虚函数的目的,使派生类继承函数的接口和强制性实现
如果抽象类里面有虚函数,建议把析构函数定义成虚函数,
防止出现内存泄漏
using namespace std;
class Base
{
public:
virtual ~Base() { cout << "delete base" << endl; }
};
class Child :public Base
{
public:
~Child(){ cout << "delete Child" << endl; }
};
int main()
{
Base * c = new Child();
delete c;
return 0;
}
using namespace std;
class Base
{
public:
~Base() { cout << "delete base" << endl; }
};
class Child :public Base
{
public:
~Child(){ cout << "delete Child" << endl; }
};
int main()
{
Base * c = new Child();
delete c;
return 0;
}
(1)如果父类的析构函数不加virtual关键字
当父类的析构函数不声明成虚析构函数的时候,当子类继承父类,父类的指针指向子类时,delete掉父类的指针,只调动父类的析构函数,而不调动子类的析构函数。
(2)如果父类的析构函数加virtual关键字
当父类的析构函数声明成虚析构函数的时候,当子类继承父类,父类的指针指向子类时,delete掉父类的指针,先调动子类的析构函数,再调动父类的析构函数。
因为构造函数的一个作用就是设置虚表指针,如果把构造函数设置成虚函数,那么构造函数将在虚表中存着,那么在调用构造函数的时候,虚表指针还没有设置,这样就无法找到虚表,就无法调用构造函数,而要设置虚表指针,就必须得调用构造函数,此时就已经产生矛盾了,所以构造函数不能定义为虚函数。
1.确定this指针。(先确定this指针才可以对之后的进行构建,靠ecx传递过来)
2.如果有虚函数,则让虚表指针指向虚函数表(存放在.data)
3.构造其它成员属性