一、概述:
联编就是将 模块或者函数 合并在一起生成可执行代码的处理过程(也可以叫做绑定),同时对每个模块或者函数调用分配内存地址,并且对外部访问也分配正确的内存地址,它是计算机程序彼此关联的过程。按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。
1.静态联编
是指在编译阶段就将函数实现和函数调用关联起来,因此静态联编也叫早绑定,在编译阶段就必须了解所有的函数或模块执行所需要检测的信息,它对函数的选择是基于指向对象的指针(或者引用)的类型,C语言中,所有的联编都是静态联编,并且任何一种编译器都支持静态联编。
动态联编
指在程序执行的时候才将函数实现和函数调用关联,因此也叫运行时绑定或者晚绑定,动态联编对函数的选择不是基于指针或者引用,而是基于对象类型,不同的对象类型将做出不同的编译结果。C++中一般情况下联编也是静态联编,但是一旦涉及到多态和虚函数就必须要使用动态联编了。虚函数是实现动态联编的基础
多态
字面的含义是具有多种形式、形态。C++多态有两种形式,动态多态和静态多态;动态多态是指一般的多态,动态多态是通过类继承和虚函数机制实现的多态;静态多态是通过模板来实现,因为这种多态实在编译时而非运行时,所以称为静态多态。
模板函数
参考https://blog.csdn.net/mr_h9527/article/details/82598237
二、实例
动态多态--通过类的继承和虚函数实现
#include <stdio.h> #include <iostream> class CShape//图形类、基类 { public: CShape(){} virtual ~CShape(){}//析构函数也声明为虚函数,为了析构彻底! virtual void Draw() = 0;//把想要多态的函数声明为虚函数 }; class CPoint : public CShape//点类 { public: CPoint(){} ~CPoint(){} void Draw() {printf("Hello! I am Point!\n");} }; class CLine : public CShape //线类 { public: CLine(){} ~CLine(){} void Draw() {printf("Hello! I am Line!");} }; int main() { CShape* shape = new CPoint();//手动分配内存,new 生成了一个点类对象,基类指针可以指向派生类的对象(多态性)!!! //draw point shape->Draw();//shape将会调用CPoint的Draw()函数,而不是基类的Draw方法 delete shape; //并不是删除指针,而是删除指针指向的对象,因为基类的析构函数被声明为虚函数,因此会调用该指针指向的派生类的析构函数, //而派生类的析构函数又会自动调用基类的析构函数,这样整个派生类的对象会被完全释放 shape = new CLine(); //draw Line shape->Draw();//shape将会调用CLIne 的Draw()函数 delete shape; return 0; }
一个Draw() 可以有两种实现,并且是在运行时决定的,在编译阶段不知道,只有在运行的时候才能知道我们生成的shape是那种图形,
当然要实现这种效果就需要动态联编了,在基类我们会把想要多态的函数声明为虚函数(前面加上 virtual关键字),而虚函数的实现原理就使用了动态联编。
静态多态----通过模板实现
在上面例子的基础之上添加模板函数:
template <class T> void DrawShape(T* t) { t->Draw(); }
main函数修改为
void main() { CShape* shape = new CPoint();
//draw point shape->Draw(); DrawShape<CPoint>((CPoint*)shape); //模板函数的调用方法:模板名<用来实例化模板的类型>(参数)delete shape;
shape = new CLine(); //draw Line shape->Draw(); DrawShape<CLine>((CLine*)shape); delete shape; return ; }
在程序编译main函数的时候,编译器就已经指定了DrawShape函数里面的Draw要调用那个实现了,这就是静态多态,在编译时就已经知道了要调用的函数。
三、虚函数的使用
(1)基类析构函数定义为虚函数时:基类指针可以指向派生类的对象(多态性),如果删除该基类指针delete [ ]p(并不是删除指针,而是删除指针所指向的对象);就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全被释放。
(2)基类析构函数不定义为虚函数时:编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全
原文:https://www.cnblogs.com/hemengjita/p/12756225.html