C++中虚函数与纯虚函数的用法_C 语言

本文较为深入的分析了C++中虚函数与纯虚函数的用法,对于学习和掌握面向对象程序设计来说是至关重要的。具体内容如下:

首先,面向对象程序设计(object-oriented programming)的核心思想是数据抽象、继承、动态绑定。通过数据抽象,可以使类的接口与实现分离,使用继承,可以更容易地定义与其他类相似但不完全相同的新类,使用动态绑定,可以在一定程度上忽略相似类的区别,而以统一的方式使用它们的对象。

虚函数的作用是实现多态性(Polymorphism),多态性是将接口与实现进行分离,采用共同的方法,但因个体差异而采用不同的策略。纯虚函数则是一种特殊的虚函数。虚函数联系到多态,多态联系到继承。所以本文中都是在继承层次上做文章。没了继承,什么都没得谈。

一、虚函数

1 . 定义

在C++中,基类必须将它的两种成员函数区分开来:一种是基类希望其派生类进行覆盖的函数;另一种是基类希望派生类直接继承而不要改变的函数。对于前者,基类通过在函数之前加上virtual关键字将其定义为虚函数(virtual)。

class Base{ // 基类
public:
  virtual int func(int n) const;
}; 

class Derive_Class : public Base{
public:
  int func(int n) const; // 默认也为虚函数
};

当我们在派生类中覆盖某个函数时,可以在函数前加virtual关键字。然而这不是必须的,因为一旦某个函数被声明成虚函数,则所有派生类中它都是虚函数。任何构造函数之外的非静态函数都可以是虚函数。派生类经常(但不总是)覆盖它继承的虚函数,如果派生类没有覆盖其基类中某个虚函数,则该虚函数的行为类似于其他的普通成员,派生类会直接继承其在基类中的版本。

2 . 动态绑定

当我们使用基类的引用(或指针)调用一个虚函数时将发生动态绑定(dynamic binding)。因为我们直到运行时才能知道到底调用了哪个版本的虚函数,可能是基类中的版本也可能是派生类中的版本,判断的依据是引用(或指针)所绑定的对象的真实类型。与非虚函数在编译时绑定不同,虚函数是在运行时选择函数的版本,所以动态绑定也叫运行时绑定(run-time binding)。

3 . 静态类型与动态类型

静态类型指的是变量声明时的类型或表达式生成的类型,它在编译时总是已知的;动态类型指的是变量或表达式表示的内存中的对象的类型,它直到运行时才可知。当且仅当通过基类的指针或引用调用虚函数时,才会在运行时解析该调用,也只有在这种情况下对象的动态类型才有可能与静态类型不同。如果表达式既不是引用也不是指针,则它的动态类型永远与静态类型一致。

4 . final和override

派生类中如果定义了一个函数与基类中虚函数同名但形参列表不同,编译器会认为这是派生类新定义的函数。如果我们的意图本是覆盖虚函数,则这种错误很难发现。通过在派生类中的虚函数最后加override关键字使得意图更加清晰。如果我们使用override标记了某个函数,但该函数并没有覆盖已存在的虚函数,编译器将报错。

class Base{ // 基类
public:
  virtual int func(int a, int b) const;
}; 

class Derive_Class : public Base{
public:
  int func(int a) const override; // 报错,没有覆盖虚函数
};

如果我们定义一个类,并不希望它被继承。或者希望某个函数不被覆盖,则可以把类或者函数指定为final,则之后任何尝试继承该类或覆盖该函数的操作将引发错误。

class Base final { /*  */ };   // 基类不能被继承
class Derive_Class : public Base { /* */ };   // 报错 

void func(int) const final;  // 不允许后续的其他类覆盖func(int)

5 . 回避虚函数的机制

在某些情况下,我们希望对虚函数的调用不要进行动态绑定,而是强迫其执行虚函数的某个特定版本。可以使用作用域运算符实现这一目的。

// 强行调用基类中定义的函数版本而不管baseP的动态类型是什么
int a = baseP->Base::func(42); 

如果一个派生类虚函数需要调用它的基类版本,但是没有使用作用域运算符,则在运行时该调用将被解析为对派生类版本自身的调用,从而导致无限递归。

二、纯虚函数

1 . 定义

为了方便使用多态特性,我们常常需要在基类中定义虚函数。在许多情况下,在基类中不能对虚函数给出有意义的实现。为了让虚函数在基类什么也不做,引进了“纯虚函数”的概念,使函数无须定义。我们通过在函数体的位置(即在声明语句的分号之前)书写=0就可以将一个虚函数说明为纯虚函数(pure virtual)。其中,=0只能出现在类内部的虚函数声明语句处:

class Base{ // 抽象基类
public:
  virtual int func(int n) const =0;
};

需要注意的是,我们也可以为纯虚函数提供定义,不过函数体必须定义在类的外部。

2 . 抽象基类

含有(或者未经覆盖直接继承)纯虚函数的类叫抽象基类(abstract base class)。抽象基类负责定义接口,而后续的其他类可以覆盖该接口。如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象基类。因为抽象基类含有纯虚函数(没有定义),所以我们不能创建一个抽象基类的对象,但可以声明指向抽象基类的指针或引用。

Base base;  // 错误,不能实例化抽象基类 

总结:

①.虚函数必须实现,不实现编译器会报错。

②.父类和子类都有各自的虚函数版本。由多态方式在运行时动态绑定。

③.通过作用域运算符可以强行调用指定的虚函数版本。

④.纯虚函数声明如下:virtual void funtion()=0; 纯虚函数无需定义。包含纯虚函数的类是抽象基类,抽象基类不能创建对象,但可以声明指向抽象基类的指针或引用。

⑤.派生类实现了纯虚函数以后,该纯虚函数在派生类中就变成了虚函数,其子类可以再对该函数进行覆盖。

⑥.析构函数通常应该是虚函数,这样就能确保在析构时调用正确的析构函数版本。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索c++
, 虚函数
纯虚函数
c 纯虚函数、c 虚函数和纯虚函数、c 纯虚函数 实现、c 纯虚函数定义、c 虚函数与纯虚函数,以便于您获取更多的相关知识。

时间: 2024-08-30 09:29:09

C++中虚函数与纯虚函数的用法_C 语言的相关文章

概述C++中的 public protected private friend关键字的用法_C 语言

一个C++类中的变量如果如下关键字作为修饰符. 1. public: 程序的任何地方都可以访问. 2 protected: 只有类本身及其派生类和其友元函数,友元类可以访问. 3 private: 只有类本身及其友元函数,友元类可以访问. 4 friend: 用于定义友元函数友元类. 在类里声明的一个普通函数,在前面加上friend修饰符,这个函数就可以访问该类的一切成员,包括private成员. 下面在看下c++ public private protected关键字 1.当在类的内部使用这三

详解C++中的增量运算符++和减量运算符--的用法_C 语言

前缀增量和减量运算符:++ 和 --  语法 ++ unary-expression –– unary-expression 备注 前缀递增运算符 (++) 向其操作数添加 1:此递增值是表达式的结果.操作数必须是类型不为 const 的左值.结果是与操作数相同类型的左值. 前缀递减运算符 (––) 与前缀递增运算符类似,只不过操作数将减少 1,并且结果是递减值. 前缀和后缀递增和递减运算符均会影响其操作数.它们之间的主要差异是递增或递减在表达式的计算中出现的顺序.在前缀形式中,将在表达式计算中

详解C++中二进制求补运算符与下标运算符的用法_C 语言

二进制求补运算符:~  语法 ~ cast-expression 备注 二进制反码运算符 (~)(有时称为"按位反码"运算符)将生成其操作数的按位二进制反码.即,操作数中为 1 的每个位在结果中为 0.相反,操作数中为 0 的每个位在结果中为 1.二进制反码运算符的操作数必须为整型. ~ 的运算符关键字 compl 运算符是 ~ 的文本等效项.访问程序中的 compl 运算符有两种方式:包括头文件 iso646.h,或使用 /Za 进行编译. // expre_One_Compleme

解析C++编程中的选择结构和switch语句的用法_C 语言

C++编写选择结构的程序下面,通过两个实例来说明如何编写较为复杂的C++程序. [例]编写程序,判断某一年是否为闰年. #include <iostream> using namespace std; int main( ) { int year; bool leap; cout<<"please enter year:";//输出提示 cin>>year; //输入年份 if (year%4==0) //年份能被4整除 { if(year%100=

基础:C++中的虚函数和纯虚函数用法

1. 虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称抽象类 2. 虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用. 3. 虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式被调用. 4. 虚函数和纯虚函数通常存在于抽象基类(abstract base class -AB

c++中虚函数和纯虚函数的作用与区别_C 语言

虚函数为了重载和多态的需要,在基类中是有定义的,即便定义是空,所以子类中可以重写也可以不写基类中的此函数! 纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数! 虚函数 引入原因:为了方便使用多态特性,我们常常需要在基类中定义虚函数. class Cman { public: virtual void Eat(){--}; void Move(); private: }; class CChild : public CMan { public: virtual void

c++ 虚函数与纯虚函数的区别(深入分析)_C 语言

在面向对象的C++语言中,虚函数(virtual function)是一个非常重要的概念.因为它充分体现 了面向对象思想中的继承和多态性这两大特性,在C++语言里应用极广.比如在微软的MFC类库中,你会发现很多函数都有virtual关键字,也就是说, 它们都是虚函数.难怪有人甚至称虚函数是C++语言的精髓. 那么,什么是虚函数呢,我们先来看看微软的解释: 虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本. 这个定

c++虚函数和纯虚函数的区别

虚函数为了重载和多态的需要,在基类中是由定义的,即便定义是空,所以子类中可以重写也可以不写基类中的函数! 纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数! 多态是C++的重要特性,通过基类指针来访问派生类的函数. 虚函数就是为了实现这功能而定义的函数,虚函数可以在定义时实现也可以不实现,定义了虚函数的类可以实例化. 纯虚函数更多的是表示接口的含义,纯虚函数定义时不能实现,需要在派生的子类中实现且必须实现,含有纯虚函数的类不能实例化. 虚析构函数与虚函要成对出现,以便

C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别

1.虚函数(impure virtual) C++的虚函数主要作用是"运行时多态",父类中提供虚函数的实现,为子类提供默认的函数实现. 子类可以重写父类的虚函数实现子类的特殊化. 如下就是一个父类中的虚函数: class A { public: virtual void out2(string s) { cout<<"A(out2):"<<s<<endl; } }; 2.纯虚函数(pure virtual) C++中包含纯虚函数的