六.虚函数
什么是虚函数
- 指向基类的指针在操作它的多态类对象时,会根据不同的类对象(基类指针指向派生类对象)调用其相应的函数 ,这个函数就是虚函数,虚函数用virtual修饰函数名。
- 虚函数的作用:在程序的运行阶段动态的选择合适的成员函数。在定义虚函数后,可以在基类的派生类中对虚函数进行重新定义,在派生类中重新定义的函数应与虚函数具有相同的形参个数和形参类型(参数类型的顺序也要一致),实现统一的接口。
- 普通的派生类对象,先调用基类构造再调用派生类构造。
#include<iostream>
#include<vector>
using namespace std;
class A {
public:
virtual void point();
};
void A::point() {
cout << "this is A" << endl;
}
class B :public A {
void point() {
cout << "this is B" << endl;
}
};
int main(int argc, char* argv[]) {
A* a = new A();
A* b = new B(); //基类指针指向派生类对象
a->point(); //this is A
b->point(); //this is B
getchar();
return 0;
}
- 纯虚函数
- 纯虚函数是一种特殊的虚函数。由于在很多情况下,基类不能对虚函数给出有意义的实现,只能把函数的实现留给该基类的派生类去做。 如,动物作为一个基类可以派生出来老虎、孔雀等子类,但动物本身生成对象并不合理。此时就需要将函数定义为纯虚函数。
- 纯虚函数,编译器要求在派生类中予以重载以实现多态性。纯虚函数的类称为抽象类,抽象类不能生成对象。
class<类名>
{
virtual 函数返回值类型 虚函数名(形参表)=0;
}
虚函数表
- 虚函数是通过虚函数表来实现的。该表是一个类的虚函数的地址表,解决了继承和覆盖的问题。在有虚函数的类的实例中,此表被分配在实例的内存中,所以当用一个父类指针操作一个子类的时候,虚函数表相当重要,指明了实际应该调用的函数。
C++如何实现多态
- C++通过虚函数实现多态。虚函数的本质就是通过基类访问派生类定义的函数。每个含有虚函数的类,其实例对象内部都有一个虚函数表指针。该虚函数表指针被初始化为本类的虚函数表的内存地址。
什么函数不能声明为虚函数
- 普通函数不能声明为虚函数。虚函数表被分配在类的实例的内存中,普通函数不属于类,没有实例。
- 构造函数不能声明为虚函数。构造函数一般用来初始化对象,只有在一个对象生成之后,才能发挥多态作用。
- 静态成员函数不能声明为虚函数。静态成员函数属于类不属于类的对象,对于每个类来说只有一份代码,所有的对象都共享这份代码,没有动态绑定的必要性。
- 内联函数不能声明为虚函数。内联函数在编译时被展开,虚函数在运行时才能动态的绑定函数。
- 友元函数不能声明为虚函数。友元函数不属于类的成员函数,不能被继承。
- 析构函数可以声明为虚函数,并且通常声明为虚函数。将基类的析构函数定义为虚函数时,当利用delete删除一个指向派生类定义的对象指针时,系统会调用相应的类的析构函数。而不将析构函数定义为虚函数时,只调用基类的析构函数。基类的析构函数应该定义为虚函数,这样在实现多态时不造成内存泄漏。
sizeof
- 虚函数占用4个字节(32位操作系统中),用来指定虚函数表的入口地址。跟虚函数的个数没有关系。
#include<iostream>
#include<vector>
using namespace std;
class A {
public:
virtual void point1();
virtual void point2();
virtual void point3();
};
void A::point1() {
cout << "this is A" << endl;
}
int main(int argc, char* argv[]) {
cout << sizeof(A) << endl; //4
getchar();
return 0;
}