简要解读C++的动态和静态关联以及虚析构函数

C++静态关联与动态关联、C++是怎样实现多态性的
在现实生活中,多态性的例子是很多的。我们分析一下人是怎样处理多 态性的。例如,新生被录取人大学,在人学报到时,先有一名工作人员审查材料,他的职责是甄别资格,然后根据录取通知书上注明的录取的系和专业,将材料转到有关的系和专业,办理具体的注册人学手续,也可以看作调用不同部门的处理程序办理入学手续。在学 生眼里,这名工作人员是总的人口,所有新生办入学手续都要经过他。学生拿的是统一的录取通知书,但实际上分属不同的系,要进行不同的注册手续,这就是多态。那么,这名工 作人员怎么处理多态呢?凭什么把它分发到哪个系呢?就是根据录取通知书上的一个信 息(你被录取入本校某某专业)。可见,要区分就必须要有相关的信息,否则是无法判别的。

同样,编译系统要根据已有的信息,对同名函数的调用作出判断。例如函数的重载, 系统是根据参数的个数和类型的不同去找与之匹配的函数的。对于调用同一类族中的虚函数,应当在调用时用一定的方式告诉编译系统,你要调用的是哪个类对象中的函数。例如可以直接提供对象名,如studl.display()或grad1.display()。这样编译系统在对程序进行编译时,即能确定调用的是哪个类对象中的函数。

确定调用的具体对象的过程称为关联(binding)。binding原意是捆绑或连接,即把两样东西捆绑(或连接)在一起。在这里是指把一个函数名与一个类对象捆绑在一起,建立关联。一般地说,关联指把一个标识符和一个存储地址联系起来。在计算机字典中可以査到,所谓关联,是指计算机程序中不同的部分互相连接的过程。有些书中把binding译为联编、编联、束定、或兼顾音和意,称之为绑定。作者认为:从意思上说,关联比较确切, 也好理解。但是有些教程中用了联编这个术语。 大家在看到这个名词时,应当知道指的就是本节介绍的关联。

顺便说一句题外话,计算机领域中大部分术语是从外文翻译过来的,有许多译名是译得比较好的,能见名知意的。但也有一些则令人费解,甚至不大确切。例如在某些介绍计算机语言的书籍中,把project译为“工程”,使人难以理解,其实译为“项目”比较确切。 有些介绍计算机应用的书中充斥大量的术语,初听起来好像很唬人、很难懂,许多学习 C++的人往往被大量的专门术语吓住了,又难以理解其真正含义,不少人“见难而退”。 这个问题成为许多人学习C++的拦路虎。因此,应当提倡用通俗易懂的方法去阐明复杂的概念。其实,有许多看起来深奥难懂的概念和术语,捅破窗户纸后是很简单的。建议读者在初学时千万不要纠缠于名词术语的字面解释上,而要掌握其精神实质和应用方法。

说明:与其他编程语言相比,例如Java、C#等,C++的语法是最丰富最灵活的,同样也是最难掌握的,大家要循序渐进,莫求速成,在编程实践中不断翻阅和记忆。

前面所提到的函数重载和通过对象名调用的虚函数,在编译时即可确定其调用的虚函数属于哪一个类,其过程称为静态关联(static binding),由于是在运行前进行关联的, 故又称为早期关联(early binding)。函数重载属静态关联。

在调用虚函数时并没有指定对象名,那么系统是怎样确定关联的呢?读者可以看到,是通过基类指针与虚函数的结合来实现多态性的。先定义了一个指向基类的指针变量,并使它指向相应的类对象,然后通过这个基类指针去调用虚函数(例如“pt->display()”)。显然,对这样的调用方式,编译系统在编译该行时是无法确定调用哪一个类对象的虚函数的。因为编译只作静态的语法检査,光从语句形式(例如“pt->display();”)是无法确定调用对象的。

在这样的情况下,编译系统把它放到运行阶段处理,在运行阶段确定关联关系。在运行阶段,基类指针变量先指向了某一个类对象,然后通过此指针变量调用该对象中的函数。此时调用哪一个对象的函数无疑是确定的。例如,先使pt指向grad1,再执行“pt->display()”,当然是调用grad1中的display函数。由于是在运行阶段把虚函数和类对象“绑定”在一起的,因此,此过程称为动态关联(dynamic binding)。这种多态性是动态的多态性,即运行阶段的多态性。

在运行阶段,指针可以先后指向不同的类对象,从而调用同一类族中不同类的虚函数。由于动态关联是在编译以后的运行阶段进行的,因此也称为滞后关联(late binding) 。

C++虚析构函数详解
当派生类的对象从内存中撤销时一般先调用派生类的析构函数,然后再调用基类的析构函数。但是,如果用new运算符建立了临时对象,若基类中有析构函数,并且定义了一个指向该基类的指针变量。在程序用带指针参数的delete运算符撤销对象时,会发生一个情况:系统会只执行基类的析构函数,而不执行派生类的析构函数。

[例] 基类中有非虚析构函数时的执行情况。为简化程序,只列出最必要的部分。

#include <iostream>
using namespace std;
class Point //定义基类Point类
{
public:
  Point( ){} //Point类构造函数
  ~Point(){cout<<"executing Point destructor"<<endl;} //Point类析构函数
};
class Circle:public Point //定义派生类Circle类
{
public:
  Circle( ){} //Circle类构造函数
  ~Circle( ){cout<<"executing Circle destructor"<<endl;} //Circle类析构函数
private:
  int radius;
};
int main( )
{
  Point *p=new Circle; //用new开辟动态存储空间
  delete p; //用delete释放动态存储空间
  return 0;
}

这只是一个示意的程序。p是指向基类的指针变量,指向new开辟的动态存储空间,希望用detele释放p所指向的空间。但运行结果为:

executing Point destructor

表示只执行了基类Point的析构函数,而没有执行派生类Circle的析构函数。

如果希望能执行派生类Circle的析构函数,可以将基类的析构函数声明为虚析构函数,如:

  virtual ~Point(){cout<<″executing Point destructor″<<endl;}

程序其他部分不改动,再运行程序,结果为:

executing Circle destructor
executing Point destructor

先调用了派生类的析构函数,再调用了基类的析构函数,符合人们的愿望。

当基类的析构函数为虚函数时,无论指针指的是同一类族中的哪一个类对象,系统会采用动态关联,调用相应的析构函数,对该对象进行清理工作。

如果将基类的析构函数声明为虚函数时,由该基类所派生的所有派生类的析构函数也都自动成为虚函数,即使派生类的析构函数与基类的析构函数名字不相同。

最好把基类的析构函数声明为虚函数。这将使所有派生类的析构函数自动成为虚函数。这样,如果程序中显式地用了delete运算符准备删除一个对象,而delete运算符的操作对象用了指向派生类对象的基类指针,则系统会调用相应类的析构函数。

虚析构函数的概念和用法很简单,但它在面向对象程序设计中却是很重要的技巧。

专业人员一般都习惯声明虚析构函数,即使基类并不需要析构函数,也显式地定义一个函数体为空的虚析构函数,以保证在撤销动态分配空间时能得到正确的处理。

构造函数不能声明为虚函数。这是因为在执行构造函数时类对象还未完成建立过程,当然谈不上函数与类对象的绑定。

(0)

相关推荐

  • 解析C++中虚析构函数的作用

    我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数.可是,为什么要这样做呢?下面用一个小例子来说明:    有下面的两个类: 复制代码 代码如下: class ClxBase{public:    ClxBase() {};    virtual ~ClxBase() {};    virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };}

  • C++虚析构函数的使用分析

    在C++中,不能声明虚构造函数,但可以声明虚析构函数.多态性是指不同的对象对同一消息有不同的行为特性.虚函数作为运行时多态性的基础,主要是针对对象的,而构造函数是在对象产生之前运行的,因此虚构造函数是没有意义的.析构函数的功能是在该类对象消亡之前进行一些必要的清理工作,析构函数最好都是virtual的.首先解释一下虚构函数和指针之间是如何交互的,以及虚析构函数的具体含义.例如以下代码,其中SomeClass是含有非virtual析构函数的一个类:SomeClass *p= new SomeCla

  • C++的虚析构详解及实例代码

    C++的虚析构 最近准备复习一遍所有的知识点,先从基础开始做起,用几分钟写个继承和析构吧. 父类为A,子类为B,代码如下: class A { public: A() { cout << "构造A"<< endl; } ~A() { cout << "析构A" << endl; } } class B:public A { public: B() { cout << "构造B"<&

  • 简要解读C++的动态和静态关联以及虚析构函数

    C++静态关联与动态关联.C++是怎样实现多态性的 在现实生活中,多态性的例子是很多的.我们分析一下人是怎样处理多 态性的.例如,新生被录取人大学,在人学报到时,先有一名工作人员审查材料,他的职责是甄别资格,然后根据录取通知书上注明的录取的系和专业,将材料转到有关的系和专业,办理具体的注册人学手续,也可以看作调用不同部门的处理程序办理入学手续.在学 生眼里,这名工作人员是总的人口,所有新生办入学手续都要经过他.学生拿的是统一的录取通知书,但实际上分属不同的系,要进行不同的注册手续,这就是多态.那

  • 详解C++中虚析构函数的作用及其原理分析

    C++中的虚析构函数到底什么时候有用的,什么作用呢. 一.虚析构函数的作用 总的来说虚析构函数是为了避免内存泄露,而且是当子类中会有指针成员变量时才会使用得到的.也就说虚析构函数使得在删除指向子类对象的基类指针时可以调用子类的析构函数达到释放子类中堆内存的目的,而防止内存泄露的. 我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数.可是,为什么要这样做呢?下面用一个小例子来说明: #include<iostream> using namespace std; class Cl

  • 简要解读Ruby面向对象编程中的作用域

    作用域 Ruby中不具备嵌套作用域(即在内部作用域,可以看到外部作用域的)的特点,它的作用域是截然分开的,一旦进入一个新的作用域,原先的绑定会被替换为一组新的绑定. 程序会在三个地方关闭前一个作用域,同时打开一个新的作用域,它们是: 类定义class 模块定义 module 方法定义 def 上面三个关键字,每个关键字对应一个作用域门(进入),相应的end则对应离开这道门. 扁平化作用域 从一个作用域进入另一个作用域的时候,局部变量会立即失效,为了让局部变量持续有效,可以通过规避关键字的方式,使

  • 详解JAVA 虚引用

    定义 虚引用是使用PhantomReference创建的引用,虚引用也称为幽灵引用或者幻影引用,是所有引用类型中最弱的一个.一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也无法通过虚引用获得一个对象实例. 说明 虚引用,正如其名,对一个对象而言,这个引用形同虚设,有和没有一样. 如果一个对象与GC Roots之间仅存在虚引用,则称这个对象为虚可达(phantom reachable)对象. 当试图通过虚引用的get()方法取得强引用时,总是会返回null,并且,虚引用必须和引用队列一

  • 深入了解JAVA 虚引用

    定义 虚引用是使用PhantomReference创建的引用,虚引用也称为幽灵引用或者幻影引用,是所有引用类型中最弱的一个.一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也无法通过虚引用获得一个对象实例. 说明 虚引用,正如其名,对一个对象而言,这个引用形同虚设,有和没有一样. 如果一个对象与GC Roots之间仅存在虚引用,则称这个对象为虚可达(phantom reachable)对象. 当试图通过虚引用的get()方法取得强引用时,总是会返回null,并且,虚引用必须和引用队列一

  • 浅谈C++ 虚函数分析

    虚函数调用属于运行时多态,在类的继承关系中,通过父类指针来调用不同子类对象的同名方法,而产生不同的效果. C++ 中的多态是通过晚绑定(对象构造时)来实现的. 用法 在函数之前声明关键字 virtual 表示这是一个虚函数,在函数后增加一个 = 0 表示这是一个纯虚函数,纯虚函数的类不能创建具体实例. 该示例作后文分析使用,一个包含纯虚函数的父类,一个重写了父类方法的子类,一个无继承的类. struct Base { Base() : val(7777) {} virtual int fuck(

  • C++ 基础教程之虚函数实例代码详解

    虚函数的定义 虚函数:就是在基类的成员函数前加关键字virtual(即被virtual关键字修饰的成员函数),并在一个或多个派生类中被重新定义的成员函数:虚函数:就是在编译的时候不确定要调用哪个函数,而是动态决定将要调用哪个函数.它的作用就是为了能让这个函数在它的子类里面可以被重载,这样的话,编译器就可以使用后期绑定来达到多态了,也就是用基类的指针来调用子类的这个函数:虚函数的作用:在于用专业术语来解释就是实现多态性,多态性是将接口与实现进行分离,通过指向派生类的基类指针或引用,访问派生类中同名

  • 详解C++纯虚函数与抽象类

    1.虚函数 1.1虚函数简介 虚函数可以毫不夸张的说是C++最重要的特性之一,我们先来看一看虚函数的概念. 在基类的定义中,定义虚函数的一般形式为: virtual 函数返回值类型 虚函数名(形参表) { 函数体 } 为什么说虚函数是C++最重要的特性之一呢,因为虚函数承载着C++中动态联编的作用,也即多态,可以让程序在运行时选择合适的成员函数.虚函数必须是类的非静态成员函数(且非构造函数),其访问权限是public.那么:  (1)为什么类的静态成员函数不能为虚函数?  如果定义为虚函数,那么

随机推荐