一、虚函数:
(1)虚函数的概念:
类的成员函数前面加virtual关键字,则这个成员函数称为虚函数。
(2)virtual关键字使用的域 :
virtua 只在类体内使用;就是说: 如果在类外定义虚函数,那么只能在类内声明函数时加virtual,类外定义函数时不能加virtual。
(3)那些函数可以定义为虚函数:
只有类的成员函数才能定义为虚函数。
注意:*静态成员函数不能定义为虚函数(不含this指针,无法在类外通过对象调用该静态函数)*
(4)虚函数重写:
当在子类中定义了一个与父类完全相同【要求函数名、参数列表、返回值完全相同。(协变除外:协变情况是函数名、参数列表、完全相同。返回值是父子类中的虚函数各自返回自己类的指针或者引用)的虚函数时,则称子类的这个函数重写(也称覆盖)了父类的这个虚函数。
(5)虚函数的作用(虚函数和多态的关系):
c++中的虚函数通过重写父类的虚函数,而实现多态:简单说父类的指针/引用调用重写的虚函数,当父类指针/引用指向父类对象时调用的是父类的虚数;当父类指针/引用指向子类对象时调用的是子类的虚函数。
(6)
当在派生类中定义了一个与父类虚函数同名的成员函数时,只要该成员函数的参数个数,参数类型以及返回类型,与父类中同名的虚函数完全一样。则派生类中的这个成员函数无论是否使用virtual,他都将成为一个虚函数;(简单的说就是,当子类的一个虚函数重写了父类的虚函数,那么子类中的这个虚函数可以不用加virtual修饰);
(7)
如果父类中定义一个虚函数,那么通过继承机制,在派生类中将继承父类中的虚函数,则派生类中继承自父类的虚函数始终保持虚函数的特性。
(8)c++规定构造函数不能声明为虚函数;
建议:
operator=可以定义为虚函数,但是最好不要将operator=定义为虚函数,因为容易使用时容易引起混淆。
(9)
不要在构造函数和析构函数里面调用虚函数(不光是虚函数,在构造函数和析构函数里面最好任何成员函数都不要调用),因为在构造函数和析构函数中,对象是不完整的,可能会发生未定义的行为。
#include<iostream>
using namespace std;
class A
{
public:
A()//构造函数
{
_a=1;
show();
_b=2;
}
virtual void show()//虚函数
{
cout<<"A::show()" <<endl;
cout<<_a<<"->"<<_b<<endl;
}
private:
int _a;
int _b;
};
int main()
{
A a;
return 0;
}
(10)最好把基类的析构函数声明为虚函数。
解释:
我们都知道,派生类的对象从内存中撤销时,一般都是先调用派生类的析构函数,清理释放派生类特有的成员变量;然后再调用基类的析构函数,清理释放子类对象中从基类继承下来的基类成员变量;
但是;如果用nwe操作符建立了一个派生类对象,而且定义了一个基类指针指向这个对象,那么当用delete操作符撤销子类对象时,系统只调用父类的析构函数,而不调用派生类的析构函数,那么就只撤销了子类对象中继承于父类的成员变量;而派生类中特有的成员变量没有被清理撤销;
怎么解决这种问题呢?很简单,只需要将基类的析构函数声明为虚函数;因为,一旦将基类的析构函数声明为虚函数,那么由该基类所派生的所有派生类的析构函数也都自动成为虚函数,此时子类的析构函数对父类的析构函数进行了覆盖(形成多态);此时当父类指针指的是同一族中的那个子类的对象,系统就会采用动态联编(动态多态),调用正确的该子类对象类的析构函数,清理子类特有的成员变量;然后在该子类析构函数中调用父类构造函数对该子类继承于父类的成员变量进行清理;这样才是完全清理掉了子类对象空间;
#include<iostream>
using namespace std;
class A
{
public:
virtual void show()
{
cout<<"A::show()" <<endl;
}
virtual ~A()
{
cout<<"~A()"<<endl;
}
};
class B:public A
{
public:
virtual void show()//重写A中的show()
{
cout<<"B::show()"<<endl;
}
~B()//虚析构函数,重写~A()
{
cout<<"~B()"<<endl;
}
};
int main()
{
A* p;//定义父类指针
p=new B();
p->show();//形成多态,调用B::show()
delete p;//释放子类无名对象;
return 0;
}
二、纯虚函数
(1)纯虚函数的概念:
在虚函数的原型声明后加上”=0”,则成员函数为纯虚函数;纯虚函数没有函数体;
virtual 返回类型 函数名(形式参数列表)=0;//函数声明
(2)c++为什么要有纯虚函数:
因为,在实际开发过程中,在许多情况下,不能在基类中为虚函数给出一个有意义的定义,这时可以将它说明为纯虚函数,将具体定义留给派生类去做;
(3)纯虚函数的作用:
纯虚函数的作用是在基类中为其派生类保留一个函数的名字;以便派生类根据需要对它进行定义;纯虚函数为接口函数;为派生类提供了一个定义的接口;
(4)抽象类:
包含纯虚函数的类叫做抽象类(也叫接口类);
(5)抽象类的特点:
抽象类不能实例化出对象;抽象类只能作为基类来派生新类;
(6)
纯虚函数只有在派生类中重新定义以后,该派生类就不是抽象类;该派生类才能实例化出对象。
如果在派生类中没有重新定义父类的纯虚函数;那么派生类继承父类的纯虚函数在派生类当中就是纯虚函数;则该派生类为抽象类;不能实例化出对象;