C++ 之 【virtual小结】- 虚函数虚继承
C++virtual小结
发布日期:2021-11-03 21:19:18
浏览次数:14
分类:技术文章
本文共 3333 字,大约阅读时间需要 11 分钟。
https://blog.csdn.net/false_mask/article/details/81662117
C++virtual小结 被virtual修饰的分为两类:虚函数和虚继承
被virtual修饰的分为两类:虚函数和虚继承
一:虚函数
当函数被virtual关键字修饰时,该函数被称为虚函数,MSDN上解释如下:
虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本。若继承类未对虚函数重新定义,调用的仍是基类中的定义。
在成员函数(必须为虚函数)的形参列表后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。纯虚函数是一定要被继承的,否则它存在没有任何意义。
例:
#includeusing namespace std;class Parent//声明父类{public: void Function1() { cout << "执行Parent.Function1\n"; } virtual void Function2() { cout << "执行Parent.Function2\n"; }};class Child :public Parent//声明子类{ void Function1() { cout << "执行Child.Function1\n"; } void Function2() { cout << "执行Child.Function2\n"; }};Parent *p;//声明Parent指针int main(){ cout << "输入1,使指针指向Parent类1,输入其他,使指针指向Child子类"; if ((cin.get()) == '1') p = new Parent; else p = new Child; p->Function1(); p->Function2(); cin.get(); cin.get(); main();//递归main(),实现循环 return 0;} 输出结果如下: 输入1,使指针指向Parent类1,输入其他,使指针指向Child子类 输入:1 执行Parent.Function1 执行Parent.Function2 输入1,使指针指向Parent类1,输入其他,使指针指向Child子类 输入:2 执行Parent.Function1 执行Child.Function2 可以看到: 我们声明了一个Parent指针,当输入为1时,指针指向Parent对象,两个函数正确执行。 然而,当输入2时,指针实际却指向了Child类,此时,Function1执行的却是Parent中的。FUnction2正确执行。 为何指向Child的1Function1函数执行错误了呢? 因为我们是用一个Parent指针调用Function1,虽然这个指针指向的是Child类的对象,但是编译器无法知道这一事实(直到运行时程序才能根据用户的输入判断指针指向的对象),它只能按照调用Parent的函数来理解并编译。所以会出错。 为何指向CHild的Function2函数正确执行了呢? 注意到:Parent类中的Function2使用了Virtual关键字修饰,也就是说,它是一个虚函数。虚函数最关键的特点是“动态联编”,它可以在运行时判断指针指向的对象,并正确调用对象的函数。 二:虚继承 虚继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。 1
间接或直接地继承同一基类,会在子类中存在多份拷贝。这样将存在两个问题:①浪费存储空间;②存在二义性
①浪费存储空间
例: 如图:类D继承来自类B1、B2,而类B1、B2都继承来自类A,因此会出现如图所示局面(A为非虚基类): 为了节省内存空间,可以将B1、B2对A的继承定义为虚继承,那么A就成了虚拟基类。成为下图所示的局面。 实现代码如下:class A{};class B1 :public virtual A{};class B2 :public virtual A{};class D :public B1,public B2{}; ②二义性 仍然以A、B1、B2、D为例,代码如下: //CForQt.cpp#includeusing namespace std;class A{public: void f() { cout << "A.f()" << endl; }private: int k;//若基类没有数据成员,则在这里多重继承编译不会出现二义性};class B1 :public A{};class B2 :public A{};class D :public B1,public B2{};int main(){ D d; d.f(); cin.get(); return 0;} 编译时会出现如下错误: 1>—— 已启动生成: 项目: CForQt, 配置: Debug Win32 —— 1>CForQt.cpp 1>c:\users\31951\source\repos\cforqt\cforqt\cforqt.cpp(25): error C2385: 对“f”的访问不明确 1>c:\users\31951\source\repos\cforqt\cforqt\cforqt.cpp(25): note: 可能是“f”(位于基“A”中) 1>c:\users\31951\source\repos\cforqt\cforqt\cforqt.cpp(25): note: 也可能是“f”(位于基“A”中) 1>已完成生成项目“CForQt.vcxproj”的操作 - 失败。 ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ========== 从报错中可以看到:编译出错的问题在于对“f”的访问不明确 之所以出现上述问题,是因为编译器在进行编译时,需要确定子类的函数定义,若A:f()是确定的,那么在编译B1、B2是会在编译器的语法树上生成B1::f()和B2::f()等标识,那么在编译D时,由于B1、B2都有一个函数f(),此时编译器将试图生成两个D::f()标识,显然这时就要报错了。(当我们不使用D::f()时,以上标识都不会生成,所以,如果去掉d.f()一句,编译器不会报错) 为了解决上述问题,可以使用虚继承来解决: #include using namespace std;class A{public: void f() { cout << "A.f()" << endl; }private: int k;//若基类没有数据成员,则在这里多重继承编译不会出现二义性};class B1 :public virtual A{};class B2 :public virtual A{};class D :public B1,public B2{};int main(){ D d; d.f(); cin.get(); return 0;} 此时,编译器输出结果为: A.f() 参考: 注:本人能力有限,若博文有错,敬请指正
转载地址:https://blog.csdn.net/wjydym/article/details/90768127 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
表示我来过!
[***.240.166.169]2024年03月22日 05时55分22秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
一种改进中文 API 可读性的方法:参数不限于在末尾
2019-04-26
中文编程开发工具的生存模式探讨
2019-04-26
写给木兰编程语言研发团队的公开信
2019-04-26
为什么要急着为「木兰」编程语言贴上“造假”的标签?
2019-04-26
编程语言国产化的关键一战——对肆意污名化“木兰”编程语言说“不”
2019-04-26
各大媒体对「木兰」编程语言的不当言论盘点
2019-04-26
戳破针对「木兰」编程语言的拙劣谣言
2019-04-26
为「木兰」编程语言添加对中文命名标识符的支持
2019-04-26
悬赏万元,重现「木兰」编程语言编译器
2019-04-26
跳出编程语言本身看中文编程语言设计
2019-04-26
RPLY 入门例程中文化
2019-04-26
木兰编程语言入门教程之一——浅介
2019-04-26
木兰编程语言入门教程之二——控制走向
2019-04-26
基于「木兰」编译器,加十行代码实现 ∈ (属于集合)语法
2019-04-26
创建安卓键盘演示——“好不”
2019-04-26
木兰编程语言入门教程之三——函数和类型
2019-04-26
基于「木兰」逆向工程用 pyinstaller 生成可执行文件
2019-04-26
从微盟事件看商业数据公开化的必然趋势
2019-04-26
为新语言编写Visual Studio Code语法高亮插件
2019-04-26
手机编程环境初尝试-用AIDE开发Android应用
2019-04-26