virtual
定义C++中虚函数的关键字
virtual是定义C++中虚函数的关键字 。在面向对象程序设计领域,C++Object Pascal 等语言中有虚函数(英语:virtual function)或虚方法(英语:virtual method)的概念。这种函数方法可以被子类继承覆盖,通常使用动态调度实现。这一概念是面向对象程序设计中(运行时)多态的重要组成部分。简言之,虚函数可以给出目标函数的定义,但该目标的具体指向在编译期可能无法确定。
目的
虚函数概念的引入可以解决这样的问题:
面向对象程序设计中,派生类继承自基类。使用指针引用访问派生类对象时,指针或引用本身所指向的类型可以是基类而不是派生类。如果派生类覆盖了基类中的方法,通过上述指针或引用调用该方法时,可以有两种结果:
虚函数的效果属于后者。如果问题中基类的函数是“虚”的,则调用到的都是最终派生类(英语:most-derived class)中的函数实现,与指针或引用的类型无关。反之,如果函数非“虚”,调用到的函数就在编译期根据指针或者引用所指向的类型决定。
有了虚函数,程序甚至能够调用编译期还不存在的函数。
C++中,在基类的成员函数声明前加上关键字virtual即可让该函数成为虚函数,派生类中对此函数的不同实现都会继承这一修饰符,允许后续派生类覆盖,达到迟绑定的效果。即便是基类中的成员函数调用虚函数,也会调用到派生类中的版本。
程序示例
例如,一个基类Animal有一个虚函数eat。子类Fish要实做一个函数eat(),这个子类Fish与子类Wolf是完全不同的,但是你可以引用类别 Animal 底下的函数eat()定义,而使用子类Fish底下函数eat()的进程。
C++
以下代码是 C++ 的程序示例。要注意的是,这个示例没有异常处理的代码。尤其是new或是vector::push_back丢出一个异常时,程序在运行时有可能会出现崩溃或是错误的现象。
以下是虚函数Animal::eat()的输出:
当Animal::eat()不是被宣告为虚函数时,输出如下所示:
Java
输出:
C#
在 C# 语言中, 对基类中的任何虚方法必须用virtual修饰, 而派生类中由基类继承而来的重载方法必须用override修饰. 以下是 C# 的一个程序实例:
输出:
抽象类和纯虚函数
纯虚函数或纯虚方法是一个需要被非抽象的派生类覆盖(override)的虚函数. 包含纯虚方法的类被称作抽象类; 抽象类不能被直接实例化。 一个抽象基类的一个子类只有在所有的纯虚函数在该类(或其父类)内给出实现时, 才能直接实例化. 纯虚方法通常只有声明(签名)而没有定义(实现),但有特例情形要求纯虚函数必须给出函数体定义.
虽然纯虚方法通常在定义它的类中没有实现, 在 C++ 语言中, 允许纯虚函数在定义它的类中包含其实现, 这为派生类提供了备用或默认的行为. C++的虚基类的虚析构函数必须提供函数体定义,否则链接时(linking)在析构该抽象类的派生实例对象的语句处会报错。
C++
C++语言中, 纯虚函数用一种特别的语法[=0]定义(但 VS 也支持 abstract 关键字:virtual ReturnType Function()abstract;), 见以下示例.
纯虚函数的定义仅提供方法的原型. 虽然在抽象类中通常不提供纯虚函数的实现, 但是抽象类中可以包含其实现, 而且可以不在声明的同时给出定义. 每个非抽象子类仍然需要重载该方法, 抽象类中实现的调用可以采用以下这种形式:
构造与析构时的行为
在对象的构造函数析构函数中涉及虚函数时,不同的语言有不同的规定。在以 C++ 为代表的一些语言中,虚函数调度机制在对象的构造和析构时有不同的语义,一般建议尽可能避免在 C++ 的构造函数中调用虚函数。而在C#Java等另一些语言中,构造时可以调用派生类中的实现,抽象工厂模式设计模式也鼓励在支持这一特性的语言中使用这种用法。
参考资料
最新修订时间:2024-03-26 10:26
目录
概述
目的
参考资料