C++笔记——虚函数
C++能够通过继承等方法实现快速开发,为了满足多态和泛型编程这一特性,C++使用虚函数来完成这一操作。虚函数是运行时决定,其他语言是通过 编译时决定的。
虚函数的实现
虚函数的实现是由两个部分组成的,虚函数指针与虚函数表。
虚函数指针
虚函数指针从本质上来说就是一个指向函数的指针,与普通的指针并无区别。它指向所定义的虚函数,具体是在子类里实现,当子类调用虚函数的时候,实际上是通过调用该虚函数指针从而找到接口。虚函数指针是确实存在的一种数据类型,在一个被实例化的对象中,它总是被存放在该对象地址首位,这种做法的目的是为了保证运行的快速性。与对象的成员不同,虚函数指针对外部是完全不可见的,除非通过直接访问地址的做法,否则它不可见也不能被外部调用。只有拥有虚函数的类才会拥有虚函数指针,每一个虚函数也都会对应一个虚函数指针。所有拥有虚函数的类的所有对都会因为虚函数产生额外的开销,并且也会在一定程度上降低程序的速度。与java不同,C++将是否使用虚函数这一权利交个开发者。
虚函数表
每个类的实例化对象都会拥有虚函数指针并且都排列在对象的地址首部。而它们也都是按照一定的顺序组织起来的,从而构成了一种表状结构,称为虚函数表 。
C++中一个类是共用一张虚函数表的,基类有基类的虚函数表,子类是子类的虚函数表,这极大的节省了内存
虚函数的定义与声明
class Base
{
public:
virtual void func()const
{
cout << "Base!" << endl;
}
};
class Derived :public Base
{
public:
virtual void func()
{
cout << "Derived!" << endl;
}
};
void show(Base& b)
{
b.func();
}
Base base;
Derived derived;
int main()
{
show(base);
show(derived);
base.func();
derived.func();
return 0;
}
函数输出:
Base!
Base!
Base!
Derived!
也不一定虚函数的声明与定义要求非常严格,只有在子函数中的虚函数与父函数一模一样的时候(包括限定符const)才会被认为是真正的虚函数,不然的话就只能是重载。这被称为虚函数定义的同名覆盖原则,意思是只有名称完全一样时才能完成虚函数的定义。