C++:虚函数、重写覆盖、动态绑定(关键字virtual、override、final)

更新时间:2023-07-23 03:05:53 阅读: 评论:0

C++:虚函数、重写覆盖、动态绑定(关键字virtual、override、final)
⼀、虚函数
概念:在函数前⾯加virtual,就是虚函数
虚函数的⼀些概念:
只有成员函数才可定义为虚函数,友元/全局/static/构造函数都不可以
虚函数需要在函数名前加上关键字virtual
成员函数如果不是虚函数,其解析过程发⽣在编译时⽽⾮运⾏时
派⽣类可以不覆盖(重写)它继承的虚函数
⼦类重写(覆盖)虚函数的规则:
虚函数在⼦类和⽗类中的访问权限可以不同车娅婷
基类与派⽣类的虚函数名与参数列表相同。函数返回值有以下要求:
①如果虚函数的返回值类型是基本数据类型:返回值类型必须相同
②如果虚函数的返回值类型是类本⾝的指针或引⽤:返回值类型可以不痛,但派⽣类的返回值类型⼩于基类返回值类型
class A {
public:
int a;
public:
A(int num) :a(num) {};
virtual A& func() {};
};
class B:public A{
public:
int b;
public:
B(int num) :A(num) {};
virtual B& func() {};
};
⼆、为什么要设计虚函数
我们知道派⽣类会拥有基类定义的函数,但是对于某些函数,我们希望派⽣类各⾃定义适合于⾃⼰版本的函数,于是基类就将此函数定义为虚函数,让派⽣类各⾃实现⾃⼰功能版本的函数(但是也可以不实现)
我们通常在类中将这两种成员函数分开来:
⼀种是基类希望派⽣类进⾏覆盖的虚函数
⼀种是基类希望派⽣类直接继承⽽不要改变的函数
三、覆盖(重写)
概念:基类的虚函数,如果派⽣类有相同的函数,则⼦类的⽅法覆盖了⽗类的⽅法同学聚会致辞
覆盖(重写)与隐藏的关系:
星露谷温室
覆盖与隐藏都是⼦类出现与⽗类相同的函数名,但是有很多的不同
隐藏可以适⽤于成员变量和函数,但是覆盖只能⽤于函数
覆盖(重写)在多态中有很重要的作⽤
四、virtual、override关键字
virtual:
放在函数的返回值前⾯,⽤于表⽰该成员函数为虚函数
⽗类虚函数前必须写;⼦类虚函数前可以省略(不困省不省略,该函数在⼦类中也是虚函数类型)
virtual只能出现在类内部的声明语句之前⽽不能⽤于类外部的函数定义
override:
⽗类的虚函数不可使⽤
放在⼦类虚函数的参数列表后(如果函数有尾指返回类型,那么要放在尾指返回类型后),⽤来说明此函数为覆盖(重写)⽗类的虚函数。如果类⽅法在类外进⾏定义,那么override不能加
不⼀定强制要求⼦类声明这个关键字,但是建议使⽤(见下⾯的五)
这是C++11标准填⼊的
override设计的最初原因:
有些情况下,我们的⽗类定义了⼀个虚函数,但是⼦类没有覆盖(重写)这个虚函数,⽽⼦类中却出现了⼀个与基类虚函数名相同、但是参数不同的函数,这仍是合法的。编译器会将派⽣类中新定义的这个函数与基类中原有的虚函数相互独⽴,这时,派⽣类的函数没有覆盖掉基类的虚函数版本,虽然程序没有出错,但是却违反了最初的原则
因此C++11标准添加了⼀个override关键字放在派⽣类的虚函数后,如果编译器发现派⽣类重写的虚函数与基类的虚函数不⼀样(参数或其他不⼀样的地⽅),那么编译器将报错
class A{
virtual void f1(int) const;
virtual void f2();
void f3();
};
calss B:public A{
void f1(int)const override; //正确
void f2(int)override;      //错误,参数不⼀致
void f3()override;          //错误,f3不是虚函数
void f4()override;          //错误,B没有名为f4的函数
};
五、禁⽌覆盖(final关键字)
如果我们定义的⼀个虚函数不想被派⽣类覆盖(重写),那么可以在虚函数之后添加⼀个final关键字,声明这个虚函数不可以被派⽣类所覆盖(重写)
如果函数有尾指返回类型,那么要放在尾指返回类型后
演⽰案例
class A
{
virtual void func1()final {};
};
class B:public A研究生信息网
{
virtual void func1()override {}; //报错,func1被A声明为final类型
};
class A
{
virtual void func1() {};
};
class B:public A
{
virtual void func1()override final {}; //正确
};
class C :public B
{
virtual void func1()override {}; //报错,func1被B声明为final类型
};
六、虚函数的默认实参
和其他函数⼀样,虚函数也可以拥有默认实参,使⽤规则如下:
<ul><li>如果派⽣类调⽤虚函数没有覆盖默认实参,那么使⽤的参数是基类虚函数的默认实参;如果覆盖了虚函数的默认实参,那么就使⽤⾃⼰传⼊的参数</li>
<li>派⽣类可以改写基类虚函数的默认实参,但是不建议,因为这样就违反了默认实参的最初⽬的</li>
</ul></li>
<li><span ><strong>建议:</strong></span>如果虚函数使⽤了默认实参,那么基类和派⽣类中定义的默认实参最好⼀致</li>
class A
{
virtual void func1(int a, int b = 10) {};
};
class B:public A
{
virtual void func1(int a,int b=10)override {}; //没有改变
};
class C :public B
{
virtual void func1(int a, int b = 20)override {}; //改变了默认实参,不建议
};
class D :public C
{
virtual void func1(int a, int b)override {}; //删去了默认实参,那么在调⽤fun1时,必须传⼊a和b
};
七、动态绑定
概念:当某个虚函数通过指针或引⽤调⽤时,编译器产⽣的代码直到运⾏时才能确定到该调⽤哪个版本的函数(根据该指针所绑定的对象)
必须清楚动态绑定只有当我们通过指针或引⽤调⽤“虚函数”时才会发⽣,如果通过对象进⾏的函数调⽤,那么在编译阶段就确定该调⽤哪个版本的函数了(见下⾯的演⽰案例)
动态绑定与“派⽣类对象转换为基类对象”是相似的,原理相同,“派⽣类对象转换为基类对象”可以参见⽂章:
演⽰案例
class A
{
public:
void show()const{
cout << "A";
};
};
class B :public A //B继承于A
{
public:
void show()const{
cout << "B";
};
};
void printfShow(A const& data)
{
牟二黑data.show();
}
int main()
{
A a;
B b;
printfShow(a);
printfShow(b);
return 0;
}
上⾯的程序中,B继承于A,并且B隐藏了A的show()函数。当我们运⾏程序时,可以看到程序打印的是“AA”。所以可以得出,⾮虚函数的调⽤与对象⽆关,⽽是取决于类的类型(这个在程序的编译阶段就已经确定了),此处函数的参数类型为A,所有打印的永远是A⾥⾯的show()函数
现在我们修改程序,将基类A的show函数改为虚函数形式
class A
小学生自荐信
{
public:
virtual void show()const{
cout << "A";
};
};
现在再来运⾏程序,可以看到程序打印的是“AB”。这就是动态绑定产⽣的效果,对于虚函数的调⽤是在程序运⾏时才决定的
⼋、回避虚函数的机制
上⾯我们介绍过,我们通过指针调⽤虚函数,会产⽣动态绑定,只有当程序运⾏时才回去确定到底该调⽤哪个版本的函数
某些情况下,我们希望对虚函数的调⽤不要进⾏动态绑定,⽽是强迫其执⾏虚函数的某个特定版本。这种⽅式的调⽤是在编译时解析的。⽅法是通过域运算符来实现
通常,只有成员函数(或友元)中的代码才需要使⽤作⽤域运算符来回避虚函数的机制
什么时候需要⽤到这种回避虚函数的机制:
增肥好方法通常,基类定义的虚函数要完成继承层次中所有的类都要完成的共同的任务,⽽各个派⽣类在虚函数中各⾃添加⾃⼰的功能。此时,派⽣类希望使⽤基类的虚函数来完成⼤家共同的任务,那么就通过域运算符来调⽤基类的虚函数
#include <iostream>
using namespace::std;
class A
{
public:
virtual void func1() { cout << "A" << endl; };
};
class B:public A
{
public:
virtual void func1()override { cout << "B" << endl; };
};
int main()
{
A *p;
B b;
p = &b;
p->A::func1();  //正确,打印A
//p->B::func1(); //错误的⽤法
p->func1();      //正确,打印B
return 0;
}
立体剪纸

本文发布于:2023-07-23 03:05:53,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/1111977.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:函数   基类   覆盖   类型   默认   实参
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图