虚函数:被virtual修饰的基类成员函数。
纯虚函数:
virtual void foo() = 0。纯虚函数所在类称为**“抽象类”**,抽象类不能实例化出对象,它的子类必须重写纯虚函数,否则依然是抽象类。注:只在子类中声明virtual,无法构成虚函数。
1、普通函数
普通函数不属于类。
2、构造函数
虚函数是为了依据不同的对象来产生不同的状态,但是前提是得有这个对象,而对象的产生必须依靠构造函数;其次,虚函数表指针的初始化也在构造函数的初始化列表阶段,二者矛盾了。
3、内联成员函数
内联函数是在编译期间直接在调用位置展开代码,而虚函数是在运行时通过不同对象来判断函数的调用;其次,内联函数没有地址,不能形成虚表。
4、静态成员函数
static成员函数是类的所有对象共享的,而虚函数是针对不同对象构成多态,二者矛盾;其次,静态成员函数没有this指针,无法访问虚函数表。
5、友元函数
友元函数不属于类的成员函数,不能被继承,更不能声明为虚函数。
虚函数重写必须满足:返回值类型、函数名、参数列表都相同,但是协变和析构函数是例外。
如果基类的虚函数返回的是一个基类的指针或引用,派生类的虚函数返回的是一个派生类的指针或引用,那么这两个函数尽管返回值类型不同,但是依然构成虚函数,这种称为协变。
而虚构函数会被编译器统一命名为destructor,因此满足虚函数的条件。
语法上支持,但是会失去多态的效果,因为:
如果基类的析构函数是用virtual修饰的虚函数,那么无论派生类的析构函数是否有virtual修饰,它都是虚函数。原因是:
virtual修饰,那么派生类的virtual可以省略。在如下情况,第二点格外重要:
class A{
public:
~A() {}
};
class B :public A{
public:
B() {
_a = new int[100];
}
~B() {
delete[]_a;
}
private:
int* _a;
};
int main(){
A* p = new B;
delete p;
return 0;
}
如果析构函数不定义成虚函数,那么这里就构成不了多态,我们delete p只会调用p类型的析构函数,即~A(),而B中new的空间就造成了内存泄漏。