c++类继承中的虚函数
方法在基类中被声明为virtual的后,它在派生类中将自动成为虚方法。此时我们在派生类中将此方法声明不声明为virtual都没关系了,但是最好是声明出来好标记哪些方法是虚的。
如果一个方法在基类中没有使用关键字virtual,程序将根据声明时指针类型(也就是等号左边)选择方法。如果使用了virtual,程序将根据指针指向的对象的类型来选择方法。使用引用声明时的效果是一样的,因为引用的底层就是用指针实现的。
例如,如果析构函数不是virtual的,对象被删除时,则将只调用对应于指针类型的析构函数。如果析构函数是virtual的,则将调用相应对象类型的析构函数。下面代码中,会调用Derived的析构函数,然后再自动调用基类的析构函数。使用虚析构函数可以确保正确的析构函数序列被调用。因此,要作为基类的类的析构函数都应声明为虚函数。
#include<iostream>
using namespace std;
class Base {
public:
Base() { cout << "base contructor" << endl; };
virtual ~Base() { cout << "base destructor" << endl; };
virtual void func() { cout << "base function" << endl; }
};
class Derived :public Base {
public:
Derived() { cout << "derived contructor" << endl; };
virtual ~Derived() { cout << "derived destructor" << endl; };
virtual void func() { cout << "derived function" << endl; }
};
int main() {
Base* p=new Derived; //输出base constructor,derived constructor
p->func(); //输出derived function
delete p; //输出derived destructor,base destructor
return 0;
#include<iostream>
using namespace std;
class Base {
public:
Base() { cout << "base contructor" << endl; };
~Base() { cout << "base destructor" << endl; };
void func() { cout << "base function" << endl; }
};
class Derived :public Base {
public:
Derived() { cout << "derived contructor" << endl; };
~Derived() { cout << "derived destructor" << endl; };
void func() { cout << "derived function" << endl; }
};
int main() {
Base* p=new Derived; //输出base constructor,derived constructor
p->func(); //输出base function
delete p; //输出base destructor
return 0;
}
构造函数是不能被声明为虚函数的,创建一个派生类时,将调用派生类的构造函数而不是基类的,然后,派生类的构造函数会先使用基类的构造函数再使用自己的。因此,派生类不继承基类的构造函数,所以将类构造函数声明为虚函数是没什么意义的。
按值赋值与按指针或引用赋值的不同:
按值赋值导致只将Derived对象d的Base部分传给bv。所以调用的func函数也是Base的。但是派生类引用或指针在赋值时,可以被转换为基类引用或指针(隐式向上强制转换)。
#include<iostream>
using namespace std;
class Base {
public:
virtual void func() { cout << "base function" << endl; }
};
class Derived :public Base {
public:
virtual void func() { cout << "derived function" << endl; }
};
int main() {
Derived d;
Base *bp=&d;
bp->func(); //输出derived function
Base bv = d;
bv.func(); //输出base function
}
虚函数的工作原理:
虚函数使用的是动态联编(晚期联编)。编译器处理虚函数的方法是,给每个对象添加一个指向虚函数表的指针。虚函数表中存储了该类中所有虚函数的地址。(每个类的对象对应一个指针,每个类对应一个虚函数表)例如,基类对象包含一个指针,该指针指向基类的虚函数表。派生类对象会包含一个指向派生类虚函数表的指针,如果派生类重新定义了基类中某些的虚函数,该函数表将保存新的函数的地址。对于派生类没有重新定义的基类虚函数,该表将会保存原始版本函数的地址。