C 虚函数和纯虚函数区别
虚函数原理

虚函数原理虚函数是 C++ 中一个非常重要的特性,它为面向对象编程提供了很强的支持。
虚函数的实现原理是通过虚函数表实现的,本文将介绍虚函数的概念、使用方法以及实现原理。
一、虚函数概念虚函数是指在基类中使用 virtual 关键字声明的成员函数,它的作用是允许在子类中对该函数进行覆盖。
具体来说,虚函数允许在子类中定义一个与基类中同名的函数,当使用子类对象调用该函数时,程序会动态的选择调用子类中的函数。
虚函数的语法如下:```class Base {public:virtual void foo();};```虚函数可以被重写(覆盖),也可以被继承,但是不能被 static 和 friend 修饰。
二、虚函数的使用使用虚函数需要满足一下条件:1.虚函数必须在公有的类成员函数列表中声明,并在类声明的内部定义。
2.虚函数必须在基类和派生类中以相同的参数列表进行定义。
下面是一个使用虚函数的简单例子:class Square: public Shape {public:Square(double s) : side(s) {}double getArea() { return side * side; }Shape 是一个基类,Square 是它的一个派生类,Square 中重写了 getArea() 函数,计算正方形的面积。
虚函数的实现原理是通过虚函数表实现的。
虚函数表是一个指针数组,存储了每个类中的虚函数指针。
当对象被创建时,会在其内存空间中创建一个指向虚函数表的指针,这个指针通常称为虚函数表指针(vptr),虚函数的调用就是通过这个指针完成的。
每个含有虚函数的类都有一个独立的虚函数表,虚函数表智能在类的第一个对象中存储,它包含了该类中所有虚函数的地址。
在派生类中,虚函数表通常继承自它的直接基类,并在此基础上添加或修改虚函数的地址。
这样如果在派生类对象中调用虚函数时,程序会先获得对象的虚函数表指针,然后通过该指针找到对应的虚函数地址来执行函数。
第8章习题答案

第八章多态1.单选题(1).下列关于运算符重载的描述中,( D )是正确的。
(A) 可以改变参与运算的操作数个数 (B) 可以改变运算符原来的优先级(C) 可以改变运算符原来的结合性(D) 不能改变原运算符的语义(2).下列函数中,不能重载运算符的函数是( b )。
(A) 成员函数(B) 构造函数(C) 普通函数 (D) 友员函数(3).要求用成员函数重载的运算符是( A )。
(A) =(B) == (C) <= (D) ++(4).要求用友员函数重载的运算符是( C )。
(A) = (B) [] (C) <<(D) ()(5).在C++中,要实现动态联编,必须使用( D )调用虚函数。
(A) 类名(B) 派生类指针(C) 对象名(D) 基类指针(6).下列函数中,不能说明为虚函数的是( C )。
(A) 私有成员函数(B) 公有成员函数(C) 构造函数(D) 析构函数(7).在派生类中,重载一个虚函数时,要求函数名、参数的个数、参数的类型、参数的顺序和函数的返回值( A )。
(A) 相同(B)不同(C) 相容(D) 部分相同(8).C++中,根据(D )识别类层次中不同类定义的虚函数版本。
(A) 参数个数(B) 参数类型(C) 函数名(D) this指针类型(9).虚析构函数的作用是(C )。
(A) 虚基类必须定义虚析构函数(B) 类对象作用域结束时释放资源(C)delete动态对象时释放资源(D) 无意义(10).下面函数原型中,( B )声明了fun为纯虚函数。
(A) void fun()=0; (B) virtual void fun()=0;(C) virtual void fun(); (D) virtual void fun(){ };(11).若一个类中含有纯虚函数,则该类称为( C )。
(A) 基类(B)纯基类(C) 抽象类(D) 派生类(12).假设Aclass为抽象类,下列正确的说明语句是( B )。
virtualfree函数的详细用法

虚函数是C++中的一个非常重要的概念,它允许我们在派生类中重新定义基类中的函数,从而实现多态性。
在本文中,我们将深入探讨virtual关键字的作用,以及virtual函数和纯虚函数的使用方法。
在C++中,virtual关键字用于声明一个虚函数。
这意味着当派生类对象调用该函数时,将会调用其在派生类中的定义,而不是基类中的定义。
这种行为使得我们能够在派生类中定制化地实现函数的逻辑,从而实现不同对象的不同行为。
对于virtual函数,我们需要注意以下几点:1. 在基类中声明函数时,使用virtual关键字进行声明。
2. 派生类中可以选择性地使用virtual关键字进行重声明,但通常最好也使用virtual,以便明确表明这是一个虚函数。
3. 当使用派生类对象调用虚函数时,将会根据对象的实际类型调用适当的函数实现。
4. 虚函数的实现通过虚函数表(vtable)来实现,这是一张函数指针表,用于存储各个虚函数的位置区域。
除了普通的虚函数外,C++还提供了纯虚函数的概念。
纯虚函数是在基类中声明的虚函数,它没有函数体,只有声明。
这意味着基类不能直接实例化,只能用作其他类的基类。
纯虚函数通常用于定义一个接口,而具体的实现则留给派生类。
接下来,让我们以一个简单的例子来说明虚函数和纯虚函数的用法。
假设我们有一个基类Shape,它包含一个纯虚函数calcArea用于计算面积。
有两个派生类Circle和Rectangle,它们分别实现了calcArea 函数来计算圆形和矩形的面积。
在这个例子中,我们可以看到基类Shape定义了一个纯虚函数calcArea,它没有函数体。
而派生类Circle和Rectangle分别实现了这个函数来计算不同形状的面积。
当我们使用Shape指针指向Circle或Rectangle对象时,调用calcArea函数将会根据对象的实际类型来调用适当的实现。
除了虚函数和纯虚函数外,C++中还有虚析构函数的概念。
纯虚函数 空函数

纯虚函数空函数一、纯虚函数纯虚函数是指在基类中声明但没有定义的虚函数,它的作用是为派生类提供一个接口,派生类必须实现这个函数。
纯虚函数的声明语法为:virtual 返回类型函数名(参数列表) =0;其中“=0”表示该函数为纯虚函数。
纯虚函数的特点:1.没有函数体。
在基类中声明但没有提供函数的具体实现,从而使得基类成为了抽象类,不能被实例化。
2.继承。
子类必须实现纯虚函数,否则也将成为抽象类,无法被实例化。
3.多态性。
子类中实现了基类的纯虚函数后,可以通过基类指针调用子类的实现。
1.抽象类。
基类中有至少一个纯虚函数时,该基类就成为了抽象类。
抽象类不能被实例化,只能被其他类继承和实现。
2.接口。
纯虚函数提供了一种接口,规定了子类必须实现的方法。
这种方法被称为“接口”。
让我们创建一个基类Figure,定义一个纯虚函数area(),用于计算图形的面积。
代码如下:class Figure{public:virtual double area() = 0;};class Circle : public Figure{public:Circle(double r){radius = r;}double area(){return 3.1415926 * radius * radius; // 计算圆的面积}private:double radius;};使用上述代码创建一个程序,可以通过基类指针调用子类实现的结果。
代码如下:以上程序会输出圆的面积,结果如下:Circle's area is:314.15926二、空函数空函数是指没有任何实际功能的函数,用于占位或在后续开发中替换为有用的函数。
空函数的定义语法为:void 函数名(){}1.通常没有函数体,函数体中只有一个空语句,表示不需要执行任何操作。
2.占位。
空函数可以用作占位函数来占据函数列表中的某些位置,等待日后补充功能。
3.代码兼容性。
空函数可以提高代码的兼容性,当代码需要调用某个函数时,即使函数还未完成,也可以使用空函数来代替。
纯虚函数的作用

纯虚函数的作用纯虚函数是一种让子类继承的特殊函数,也叫虚函数。
它们没有函数实现,只有虚函数声明。
纯虚函数可以实现多态性,是对象多态性的基础。
纯虚函数将类绑定到基类,它们之间具有一种特殊的联系,即派生类必须实现所有的虚拟函数。
也就是说,如果一个类有一个或多个纯虚函数,那么该类就是一个抽象类,不能够实例化对象。
另外,如果一个子类是从一个抽象类继承的,则必须实现其父类的所有纯虚函数。
纯虚函数的最大特点是它们可以实现多态性,它通过允许在不同类中实现同一个接口来实现多态性。
这意味着,如果一个类有一个或多个纯虚函数,那么可以使用任何子类来实现这些函数,而不必担心其他的类。
例如,有一个基类Shape,它有一个纯虚函数calculateArea,那么可以使用Rectangle、Triangle和Circle类来实例化对象并实现calculateArea函数,而不必担心与其他类的交互。
多态性可以帮助程序员更好地管理它们的代码。
例如,如果你有一组可以作为参数传递的类型,你可以避免判断类别并实施不同操作的if/else代码,而是使用多态性,只需要一种共同的接口来管理隐藏内部实现细节。
纯虚函数也有一定的局限性,它们不能定义访问修饰符,比如public、protected或private,只有“virtual”和“pure virtual”修饰符。
此外,在抽象类中,只能存在纯虚函数,并且抽象类不能实例化对象,因为它的纯虚函数没有被实现。
总的来说,纯虚函数是一种很实用的特性,有助于提高代码的可扩展性。
它能够实现多态性,帮助我们更好地管理复杂的代码,并避免无用的if/else代码。
但是,它也有一定的局限性,比如不能使用其他访问修饰符,只能在抽象类中使用,并且抽象类不能实例化对象。
虚 函 数

(6)一个虚函数无论被公有继承多少次,它仍然保持其 虚函数的特性。
my_base--------
10 20
从程序运行的结果可以看出,虽然执行语句mp=&mc;后, 指针mp已经指向了对象mc,但是它所调用的函数(mp>show()),仍然是其基类对象的show(),显然这不是我 们所期望的。出现这个问题的原因以及解决方法,我 们将在下一节介绍。在此先说明引入派生类后,使用 对象指针应注意的几个问题:
derive op2; //定义派生类derive的对象op2
ptr=&op1;
//使指针ptr指向对象op1
ptr=&op2;
//错误,不允许将base类指针ptr指
向它的私有派生类//对象op2
//…
}
(2)允许将一个声明为指向基类的指针指向公有派生类
的对象,但是不能将一个声明为指向派生类对象的指 针指向其基类的对象。
(3)声明为指向基类的指针,当其指向公有派生的
对象时,只能用它来直接访问派生类中从基类继承来
的成员,而不能直接访问公有派生类中定义的成员,
例如:
class A { //... public: void print1(); }; class B:public A{ //... public: print2(); };
可见,虚函数同派生类的结合和使C++支持运行时的多 态性,而多态性对面向对象的程序设计是非常重要的, 实现了在基类定义派生类所拥有的通用接口,而在派 生类定义具体的实现方法,即常说的“同一接口,多 种方法”。
C++ 8多态与虚函数
fun(i);
8.6.1 虚函数的定义
【例8.5_1】根据赋值兼容规则可以用基类的指针指向 派生类对象,如果由该指针撤销派生类对象,则必须将析构函 数说明为虚函数,实现多态性,自动调用派生类析构函数。 通常要求将类设计成通用的,无论其他程序员怎样调用都 必须保证不出错,所以必须把析构函数定义为虚函数。 下面把【例8.5】析构函数改造为虚函数 class Person{ //数据成员略 public: virtual ~Person(); //只需在此声明一次,派生类的析构函数全为虚函数 }; //其他成员函数略
8.6.1 虚函数的定义
成员函数设置为虚函数的要点:
1. 派生类中定义虚函数必须与基类中的虚函数同名外,还必须 同参数表,同返回类型。否则被认为是重载,而不是虚函数。 如基类中返回基类指针,派生类中返回派生类指针是允许的, 这是一个例外。 静态类型:在编译时可知的引用类型或指针类型 2. 只有类的成员函数才能说明为虚函数。这是因为虚函数仅适 class Base; Base *p; //指针p的静态类型为Base 用于有继承关系的类对象。 动态类型:指针或引用所绑定的对象类型,仅运行时可知 3. 静态成员函数,是所有同一类对象共有,不受限于某个对象, class Derived:public Base; 不能作为虚函数。 Derived d; Base *p=&d; //指针p的动态类型为Derived 4. 一个类对象的静态和动态构造是相同的,实现动态多态性时, 必须使用基类类型的指针变量或引用,使该指针指向该基类的 不同派生类的对象,并通过该指针指向虚函数,才能实现动态 的多态性。
编译时的多态性 在C++ 中有两 种多态 性 运行时的 多态性
纯虚函数 const
纯虚函数 const介绍在C++中,纯虚函数是一种特殊类型的函数,它在基类中被声明为纯虚函数,而在派生类中必须被重写。
纯虚函数是一种抽象概念,它为派生类提供了一个规范,要求派生类必须实现这个函数。
除了纯虚函数的声明外,C++还引入了const限定符,用于指定函数不会修改类的成员变量。
本文将探讨如何在类中声明和使用纯虚函数const。
纯虚函数纯虚函数是通过在函数声明中添加关键字”= 0”来定义的。
这种函数没有实际的实现,它只是为了提供一个接口,要求由派生类实现。
例如:class Shape {public:virtual double getArea() const = 0;};在上面的示例中,Shape类声明了一个纯虚函数getArea(),它没有实现。
任何派生自Shape类的类必须实现这个函数。
纯虚函数const在C++中,const限定符用于指定函数不会修改类的成员变量。
将const应用于纯虚函数的声明,表示派生类在实现这个函数时也必须遵守const限定符的要求。
例如:class Shape {public:virtual double getArea() const = 0;virtual void printInfo() const = 0;};上述代码中,Shape类除了定义了纯虚函数getArea()外,还定义了另一个纯虚函数printInfo()。
这个函数也带有const限定符,表示实现它的派生类函数也必须是const的。
实例演示为了更好地理解纯虚函数const的实际用途,我们设计一个示例来演示其用法。
假设有一个名为Shape的抽象类,我们可以从它派生出Rectangle和Circle两个具体的形状类。
这些形状类都可以计算其面积,同时可以打印形状的相关信息。
#include <iostream>#include <string>class Shape {public:virtual double getArea() const = 0;virtual void printInfo() const = 0;};class Rectangle : public Shape {public:Rectangle(double width, double height) : width(width), height(height) {}double getArea() const {return width * height;}void printInfo() const {std::cout << "Rectangle: width = " << width << ", height = " << height << std::endl;}private:double width;double height;};class Circle : public Shape {public:Circle(double radius) : radius(radius) {}double getArea() const {return 3.14 * radius * radius;}void printInfo() const {std::cout << "Circle: radius = " << radius << std::endl;}private:double radius;};int main() {Rectangle rectangle(5, 6);Circle circle(3);rectangle.printInfo();std::cout << "Area: " << rectangle.getArea() << std::endl;circle.printInfo();std::cout << "Area: " << circle.getArea() << std::endl;return 0;}在上面的代码中,我们定义了Shape抽象类,并从它派生出Rectangle和Circle两个具体的形状类。
虚函数的用法
虚函数的用法
虚函数是面向对象编程中的一个重要概念。
它允许子类重写父类中的同名函数,以实现多态性。
在C++中,使用关键字"virtual"来声明一个函数为虚函数。
虚函数的使用有以下几个关键点:
1. 多态性:虚函数的主要作用是实现多态性。
当一个指向父类的指针或引用调用一个虚函数时,实际执行的是子类中重写的函数。
这种行为允许在运行时根据对象的实际类型来确定调用的函数。
2. 动态绑定:虚函数的调用是动态绑定的,也就是说在运行时确定具体调用的函数。
与之相反的是静态绑定,它是在编译时确定调用的函数。
动态绑定使得程序具有更大的灵活性和扩展性。
3. 虚函数表:为了实现动态绑定,编译器会为每个包含虚函数的类创建一个虚函数表(vtable)。
虚函数表是一个存储函数指针的数组,每个函数指针指向对应虚函数的实际实现。
每个对象都有一个指向其类的虚函数表的指针,通过这个指针可以实现动态调用。
4. 纯虚函数:有时候父类中的虚函数并不需要有实际的实现,而只
是为了在子类中进行重写。
这种函数被称为纯虚函数,可以通过在函数声明中添加"= 0" 来表示。
包含纯虚函数的类被称为抽象类,它只能作为基类使用,不能被实例化。
综上所述,虚函数是实现多态性的关键机制之一。
通过在父类中声明虚函数并在子类中重写,可以实现基于对象实际类型的动态绑定,提高程序的灵活性和可扩展性。
析构函数 纯虚函数
析构函数纯虚函数
析构函数是C++中的一个特殊成员函数,用于在对象即将被销毁时执行清理工作。
与构造函数相对应,析构函数的名称与类名相同,前面加上波浪号(~)。
纯虚函数是在基类中声明但没有定义的虚函数。
纯虚函数没有函数体,只有函数声明,其目的是让派生类继承并重写这个函数。
如果一个类中包含了至少一个纯虚函数,那么这个类就成为抽象类,无法实例化对象。
在C++中,析构函数可以声明为纯虚函数。
这样的类称为纯虚析构函数。
纯虚析构函数的存在主要是为了实现多态性,同时确保派生类在析构时能够正确地释放资源。
当基类的析构函数为纯虚函数时,派生类必须实现自己的析构函数,否则会导致链接错误。
在派生类的析构函数中,应当调用基类的析构函数,以确保基类和派生类的资源都能正确释放。
在实际应用中,纯虚析构函数常常与工厂模式一起使用。
工厂模式是一种创建型设计模式,用于创建对象,但隐藏了对象的创建细节。
通过将基类的析构函数声明为纯虚函数,可以将对象的销毁过程交给工厂类来管理,从而实现更好的封装和解耦。
纯虚析构函数也可以用于接口类。
接口类是一种只包含纯虚函数的抽象类,用于规范派生类的行为。
通过将析构函数声明为纯虚函数,
可以强制派生类实现自己的析构函数,确保资源正确释放。
析构函数作为对象生命周期管理的一部分,与纯虚函数结合使用可以达到更好的设计效果。
通过合理地使用纯虚析构函数,可以提高代码的可维护性、可扩展性和可重用性,是面向对象编程中的重要概念之一。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
8 析构函数应当是虚函数,将调用相应对象类型的析构函数,因此,如果指针指向的是子 类对
象,将调用子类的析构函数,然后自动调用基类的析构函数。
例如:
Father *fp = new Son(); //合法的,无论父类是否为抽象类,即使是具体类,也可以指向子类。
在这个例子中,程序也许不会象你想象的那样运行,在执行 delete a 的时候,实际上只 有 A::~A()被调用了,而 B 类的析构函数并没有被调用!这是否有点儿可怕?
如果将上面 A::~A()改为 virtual,就可以保证 B::~B()也在 delete a 的时候被调用了。因此 基类的析构函数都必须是 virtual 的。
void foo(); };
// 从 B 继承,不是从 A 继承! // 也没有 virtual 关键字!
这种情况下,B::foo()是虚函数,C::foo()也同样是虚函数。因此,可以说,基类声明的虚
函数,在派生类中也是虚函数,即使不再使用 virtual 关键字。
如下声明表示一个函数为纯虚函数:
... delete *fp;
//是调用~Father()还是~Son() ???
如果使用默认的静态联编,则 delete 语句调用~Father()析构,这将释放由 Son 对象中的 Father 部分指
向的内存,但不会释放新的类成员指向的内存。
但如果析构函数是虚拟的, 则上面代码会先调用~Son()释放由 Son 组件指向的内容,然后 自动调用
这种同一代码可以产生不同效果的特点,被称为“多态”。
多态这么神奇,但是能用来做什么呢?这个命题我难以用一两句话概括,一般的 C++教 程(或者其它面向对象语言的教程)都用一个画图的例子来展示多态的用 途,我就不再重 复这个例子了,如果你不知道这个例子,随便找本书应该都有介绍。我试图从一个抽象的角 度描述一下,回头再结合那个画图的例子,也许你就更容 易理解。
~Father()析构函数来释放由 Father 组件指向的内存。
这意味着:即使基类不需要显示析构函数提供服务,也不应该依赖于默认构造函数,而应 当提供虚拟析
构函数,即使他不执行任何操作: virtual ~BaseClass(){};
注:多态方式调用是指用父类的指针或引用来指向子类的实例后,用父类的指针或引用来调 用虚函数.
1.纯虚函数声明如下: virtual ReturnType FunctionName(Parameter)=0; 纯虚函数一定没有定义,纯虚函数用来规范派生类的行为,即接口。包含纯虚函数
的类是抽象类,抽象类不能定义实例,但可以声明指向实现该抽象类的具体类的指针或引 用。
2.虚函数声明如下:virtual ReturnType FunctionName(Parameter); 虚函数必须实现,如果不实现,编译器将报错,错误提示为:
虚函数只能借助于指针或者引用来达到多态的效果,如果是下面这样的代码,则虽然是 虚函数,但它不是多态的:
class A { public:
virtual void foo(); };
class B: public A {
virtual void foo(); };
void bar() {
A a; a.foo(); }
即:如果使用指向对象的引用或指针来调用虚拟方法,程序将使用为对象类型定义的方 法,而
不使用为引用或指针类型定义的方法。也称作动态联编或晚期绑定。
参考引用
<C++实践系列>C++中的虚函数(virtual function) 作者:张笑猛 原文出处:/
假设我们有下面的类层次:
这种写法的语意是:A 告诉 B,你最好 override 我的 bar()函数,但是你不要管它如何使
那么,Байду номын сангаас使用的时候,我们可以:
A * a = new B();
a->foo();
// 在这里,a 虽然是指向 A 的指针,但是被调用的函数(foo)却是 B 的!
这个例子是虚函数的一个典型应用,通过这个例子,也许你就对虚函数有了一些概念。 它虚就虚在所谓“推迟联编”或者“动态联编”上,一个类函数的调用并不是 在编译时刻被确 定的,而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还 是哪个派生类的函数,所以被成为“虚”函数。
error LNK****: unresolved external symbol "public: virtual void __thiscall
ClassName::virtualFunctionName(void)"
3.对于虚函数来说,父类和子类都有各自的版本。由多态方式调用的时候动态绑定。
4.实现了纯虚函数的子类,该纯虚函数在子类中就编程了虚函数,子类的子类即孙子类可以 覆盖
纯虚的析构函数并没有什么作用,是虚的就够了。通常只有在希望将一个类变成抽象类 (不能实例化的类),而这个类又没有合适的函数可以被纯虚化的时候,可以使用纯虚的析 构函数来达到目的。
构造函数不能是虚的。
考虑下面的例子:
class A { public:
void foo() { bar();} private:
编译器是如何针对虚函数产生可以再运行时刻确定被调用函数的代码呢?也就是说,虚 函数实际上是如何被编译器处理的呢?Lippman 在深度探索 C++对象模型[1]中的不同章节 讲到了几种方式,这里把“标准的”方式简单介绍一下。
我所说的“标准”方式,也就是所谓的“VTABLE”机制。编译器发现一个类中有被声明为 virtual 的函数,就会为其搞一个虚函数表,也就是 VTABLE。VTABLE 实际上是一个函数 指针的数组,每个虚函数占用这个数组的一个 slot。一个类只有一个 VTABLE,不管它有多 少个实例。派生 类有自己的 VTABLE,但是派生类的 VTABLE 与基类的 VTABLE 有相同 的函数排列顺序,同名的虚函数被放在两个数组的相同位置上。在创建类实例的 时候,编 译器还会在每个实例的内存布局中增加一个 vptr 字段,该字段指向本类的 VTABLE。通过 这些手段,编译器在看到一个虚函数调用的时候,就会将 这个调用改写,针对1.1中的例子:
// A::foo()被调用
在了解了虚函数的意思之后,再考虑什么是多态就很容易了。仍然针对上面的类层次, 但是使用的方法变的复杂了一些:
void bar(A * a) {
a->foo(); // 被调用的是 A::foo() 还是 B::foo()? }
因为 foo()是个虚函数,所以在 bar 这个函数中,只根据这段代码,无从确定这里被调用的是 A::foo()还是 B::foo(),但是可以肯定的说:如果 a 指向的是 A 类的实例,则 A::foo()被调用, 如果 a 指向的是 B 类的实例,则 B::foo()被调用。
void bar(A * a) {
a->foo(); }
会被改写为:
void bar(A * a) {
(a->vptr[1])(); }
因为派生类和基类的 foo()函数具有相同的 VTABLE 索引,而他们的 vptr 又指向不同的 VTABLE,因此通过这样的方法可以在运行时刻决定调用哪个 foo()函数。
class A { public:
virtual void foo()=0; };
// =0标志一个虚函数为纯虚函数
一个函数声明为纯虚后,纯虚函数的意思是:我是一个抽象类!不要把我实例化!纯虚 函数用来规范派生类的行为,实际上就是所谓的“接口”。它告诉使用者,我的派生类都会有 这个函数。
析构函数也可以是虚的,甚至是纯虚的。例如:
class A { public:
virtual ~A()=0; };
// 纯虚析构函数
当一个类打算被用作其它类的基类时,它的析构函数必须是虚的。考虑下面的例子:
class A { public:
A() { ptra_ = new char[10];} ~A() { delete[] ptra_;} private: char * ptra_; };
虚函数的标志是“virtual”关键字。
考虑下面的类层次:
class A { public:
virtual void foo(); };
class B: public A { public:
void foo(); };
// 没有 virtual 关键字!
class C: public B { public:
虽然实际情况远非这么简单,但是基本原理大致如此。
虚函数总是在派生类中被改写,这种改写被称为“override”。我经常混淆“overload”和 “override”这两个单词。但是随着各类 C++的书越来越多,后来的程序员也许不会再犯我犯 过的错误了。但是我打算澄清一下:
• override 是指派生类重写基类的虚函数,就象我们前面 B 类中重写了 A 类中的 foo() 函数。重写的函数必须有一致的参 数表和返回值(C++标准允许返回值不同的情况,这 个我会在“语法”部分简单介绍,但是很少编译器支持这个 feature)。这个单词好象一直 没有什么合 适的中文词汇来对应,有人译为 “覆盖”,还贴切一些。 • overload 约定成俗的被翻译为“重载”。是指编写一个与已有函数同名但是参数表不 同的函数。例如一个函数即可以接受整型数作为参数,也可以接受浮点数作为参数。
virtual void bar() { ...} };
class B: public A { private:
virtual void bar() { ...} };