联编的概念

静态联编

动态联编

在 C++ 中动态联编需要虚函数的支持,这是因为虚函数的工作原理决定的,而正是因为使用了虚函数来实现动态联编,也让动态联编的效率略低于静态联编。通常,编译器处理虚函数的方法是: 给每个对象添加一个隐藏成员,隐藏成员保存了一个指向函数地址数组的指针 ,这个数组就是虚函数表(virtual function table, vtbl)。虚函数表中存储了为类对象进行声明的虚函数的地址,调用虚函数时,程序将查看存储在对象中的 vtbl 地址,然后转向相应的函数地址表,如果使用类声明中定义的第一个虚函数,则程序将使用数组中的第一个函数地址,并执行具有该地址的函数。

虚函数这个概念是 C++ 的精华之一,遇到虚函数时要注意以下几点:

1.定义一个函数为虚函数,不代表函数为不被实现的函数(可以有自己的实现)

2.定义它为虚函数是为了允许用基类的指针来调用子类的这个函数(提供了基类调用子类函数的方式)

3.定义一个函数为纯虚函数,代表函数没有被实现(声明后面接=0,例如:virtual func() = 0 此时派生类必须要实现此虚函数)

4.具有纯虚函数的类是抽象类,不能用于生成对象(即不能实例化),只能派生,它派生的类如果没有实现纯虚函数,那么他的派生类还是抽象类。

虚析构函数

虚析构函数顾名思义就是将析构函数定义为虚函数。如果我们在派生中分配了内存空间,但是基类的析构函数不是虚析构函数,就会发生内存泄漏。看下面的例子:

#include <iostream>

using namespace std;

class Base{
public:
virtual void print(){
cout << "This is Base's print function" << endl;
} /* 对比加与不加 virtual 析构函数的调用情况 */
~Base(){
// virtual ~Base(){
cout << "The destructor of Base" << endl;
}
}; class Derived : public Base{
public:
void print(){
cout << "This is Derived's print function" << endl;
} ~Derived(){
cout << "The destructor of Derived" << endl;
}
}; int main()
{
Base *p = new Derived();
p->print(); delete p; return 0;
}

不加 virtual 的运行结果:

C++的静态联编和动态联编-LMLPHP

加上 virtual 的运行结果:

C++的静态联编和动态联编-LMLPHP

在上面程序示加上 virtual 时编译器还是按照 Base 类型调用了析构函数,没有执行 Derived 类的虚析构函数,就造成了内存泄露。修改 Base 类的析构函数为虚析构函数后实现了多态,就可以确保执行正确的析构函数,完成资源的释放。

总结一下关于虚函数的一些常见问题:

1.虚函数是动态绑定的,也就是说,使用虚函数的指针和引用能够正确找到实际类的对应函数,而不是执行定义类的函数,这就是虚函数的基本功能。
2.构造函数不能是虚函数。而且,在构造函数中调用虚函数,实际执行的是父类的对应函数,因为自己还么有构造好,多态此时是被 disable 的。
3.析构函数可以是虚函数,而且,在一个复杂类结构中,这往往是必须的。
4.将基类中的一个函数定义为纯虚函数,实际上是将这个类定义位抽象类,不能实例化对象。
5.纯虚函数通常没有定义体,但也可以拥有。(如果 Base 的析构函数为纯虚函数,那么也可以在类外定义 Base::~Base(){…} 的方式来定义其定义体)
6.析构函数可以是纯虚的,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。
7.非纯的虚函数必须有定义体,不然是一个错误。
8.派生类的 override 虚函数定义必须和父类完全一致,除了一个特例,如果父类返回值是一个指针或引用,子类 override 时可以返回这个指针(或引用)的派生。如在 Base 中定义了 virtual Base* fun(){ return this; },在 Derive 中可以定义 virtual Derive* fun(){ return this; }。

补充一个具有定义体的纯虚函数的例子

#include <iostream>

using namespace std;

class Base{
public:
virtual void print() = 0;
}; void Base::print(){
cout << "This is Base's fun" << endl;
} class Derived : public Base{
public:
void print(){
/* 调用基类的纯虚函数 */
Base::print();
cout << "This is Derived fun" << endl;
}
}; int main()
{
Base *T = new Derived(); T->print(); delete T; return 0;
}

程序输出:

C++的静态联编和动态联编-LMLPHP

本篇文章参考:阿里云 - C++的静态联编和动态联编

05-08 08:08