虚函数、虚基类、纯虚函数

  • (一)、虚函数
    • 1.什么是虚函数:
    • 2.虚函数的格式:
    • 3.关于虚函数的注意事项:
    • 4.虚函数的作用:
    • 5.虚函数访问格式
    • 6.虚函数的各种疑难杂症
      • 【当指针是基类、但虚函数不在基类时】
        • 代码展示:
        • 效果展示:
      • 【当指针在派生类,虚函数在基类,指向派生类】
        • 代码展示:
        • 效果展示:
      • 1.【基类和派生类都为虚函数,指针在基类指向派生类】
        • 代码展示:
        • 效果展示:
      • 2.【基类和派生类都有虚函数,指针在基类指向基类】
        • 代码展示:
        • 效果展示:
      • 3.【基类和派生类都有虚函数,指针在派生类指向派生类】
        • 代码展示:
        • 效果展示:
      • 4.【基类和派生类都有虚函数,指针在派生类指向基类】
        • 代码展示:
        • 效果展示:
    • 7.虚函数的示范
      • 【非虚函数的调用,会适当其反】
        • 代码展示:
        • 效果展示:
    • 【虚函数的调用正中下怀】
      • 代码展示
      • 效果展示:
  • (二)、虚基类
    • 1.什么是虚基类:
    • 2.虚基类的特点
    • 3.二义性的问题
      • 1.问题所在:
        • 代码展示:
        • 效果展示:
      • 2.【二义性的解决非虚基类正确示范】
        • 代码展示
        • 效果展示:
      • 3.【二义性解决虚基类的方法正确示范】
        • 代码展示
  • (三)、纯虚函数
    • 1.纯虚函数的作用
    • 2.基本格式:
  • (四)、总结

(一)、虚函数

1.什么是虚函数:

虚函数的作用是实现动态联编,也就是在程序的运行阶段动态地选择合适的成员函数,在定义了虚函数后,可以在基类的派生类中对虚函数重新定义,在派生类中重新定义的函数应与虚函数具有相同的形参个数和形参类型。以实现统一的接口,不同定义过程。如果在派生类中没有对虚函数重新定义,则它继承其基类的虚函数

2.虚函数的格式:

class Student
{virtual void show(){}
}
class Student1 :public Student
{void show(){}
}

3.关于虚函数的注意事项:

1、 必须把动态联编的行为定义为类的虚函数。  
2、类之间存在父子类型关系,一般表现为一个类从另一个类公有派生而来。 
3、 必须先使用基类指针或者引用指向子类型的对象,然后直接或者间接使用基类指针调用虚函数

4.虚函数的作用:

可以让成员函数操作一般化用基类的指针指向不同的派生类的对象时,基类指针调用其虚成员函数,则会调用其真正指向对象的成员函数
而不是基类中定义的成员函数(只要派生类改写了该成员函数)。
若不是虚函数,则不管基类指针指向的哪个派生类对象,调用时都
会调用基类中定义的那个函数。

5.虚函数访问格式

指针变量->成员函数

6.虚函数的各种疑难杂症

【当指针是基类、但虚函数不在基类时】

代码展示:

#include <iostream>
using namespace std;
class A
{public:void fun() { cout << "调用了A" << endl; }
};
class B:public A
{public:void fun() { cout << "调用了B" << endl; }};
class C:public A
{public:virtual void fun() { cout << "调用了C" << endl; }
};
int main()
{A a,*p;B b;C c;p = &c;p->fun();
}

效果展示:

【当指针在派生类,虚函数在基类,指向派生类】

代码展示:

​
#include <iostream>
using namespace std;
class A
{public:virtual void fun() { cout << "调用了A" << endl; }
};
class B:public A
{public:void fun() { cout << "调用了B" << endl; }};
class C:public A
{public:void fun() { cout << "调用了C" << endl; }
};
int main()
{A a;B b;C c,*p;p = &c;p->fun();}

效果展示:

1.【基类和派生类都为虚函数,指针在基类指向派生类】

父类的与子类的同名虚函数,在继承的时候,子类会将父类的覆盖,

代码展示:

​
#include <iostream>
using namespace std;
class A
{public:virtual void fun() { cout << "调用了A" << endl; }
};
class B:public A
{public:void fun() { cout << "调用了B" << endl; }
};
class C:public A
{public:virtual void fun() { cout << "调用了C" << endl; }
};
int main()
{A a,*p;B b;C c;p = &c;p->fun();
}

效果展示:

2.【基类和派生类都有虚函数,指针在基类指向基类】

代码展示:

​
#include <iostream>
using namespace std;
class A
{public:virtual void fun() { cout << "调用了A" << endl; }
};
class B :public A
{public:void fun() { cout << "调用了B" << endl; }};
class C :public A
{public:virtual void fun() { cout << "调用了C" << endl; }
};
int main()
{A a, * p;B b;C c;p = &a;p->fun();
}

效果展示:

3.【基类和派生类都有虚函数,指针在派生类指向派生类】

代码展示:

​
#include <iostream>
using namespace std;
class A
{public:virtual void fun() { cout << "调用了A" << endl; }
};
class B:public A
{public:void fun() { cout << "调用了B" << endl; }};
class C:public A
{public:virtual void fun() { cout << "调用了C" << endl; }
};
int main()
{A a;B b;C c,*p;p = &c;p->fun();}
​

效果展示:

4.【基类和派生类都有虚函数,指针在派生类指向基类】

错误分析:派生类访问不到基类

代码展示:

​
#include <iostream>
using namespace std;
class A
{public:virtual void fun() { cout << "调用了A" << endl; }
};
class B :public A
{public:void fun() { cout << "调用了B" << endl; }};
class C :public A
{public:virtual void fun() { cout << "调用了C" << endl; }
};
int main()
{A a;B b;C c,*p;p = &a;p->fun();
}

效果展示:

7.虚函数的示范

【非虚函数的调用,会适当其反】

代码展示:

​
#include <iostream>
using namespace std;
class A
{private:int number;
public:A(int nu=21032114) :number(nu) {}void show(){cout << "基类,学号为:" << number<<endl;}
};
class B:public A
{private:int score;
public:B(int sc = 86) :score(sc) {}void show(){cout << "派生,成绩为:" << score<<endl;}
};
int main()
{A a;a.show();B b;b.show();A* p;          p = &b;p->show(); //我想调用派生类,但仍然是基类的函数A& p1 = b;    b.show();return 0;
}

效果展示:

【虚函数的调用正中下怀】

代码展示

#include <iostream>
using namespace std;
class A
{private:int number;
public:A(int nu=21032114) :number(nu) {}virtual void show(){cout << "基类,学号为:" << number<<endl;}
};
class B:public A
{private:int score;
public:B(int sc = 86) :score(sc) {}void show(){cout << "派生,成绩为:" << score<<endl;}
};
int main()
{A a;a.show();B b;b.show();A* p;          p = &b;p->show(); //我想调用派生类,但仍然是基类的函数A& p1 = b;    b.show();return 0;
}

效果展示:

#include using namespace std;

class A { public:
virtual int fun() { return 1; } };

class E : public A { public:
virtual int fun() { return 5; } };

class B : virtual public A { public:
virtual int fun() { return 2; } };

class C : virtual public A { public:
virtual int fun() { return 3; } };

class D : public B, public C { public:
virtual int fun() { return 4; } };

int main() {
cout << sizeof(A) << endl; // 4
cout << sizeof(B) << endl; // 4
cout << sizeof© << endl; // 4
cout << sizeof(D) << endl; // 8
cout << sizeof(E) << endl; // 4

return 0; }

解释:

1.static静态成员变量不需计算

2.成员函数不占用空间

3.虚函数 有一个虚表指针占四个字节

A=4——> 4(A虚表指针)。

4.父类的与子类的同名虚函数,在继承的时候,子类会将父类的覆盖,

B=4------> 4(B虚表指针)

C=4------> 4(C虚表指针)

5.如果子类和父类都包含虚函数,但他们存放在一个虚表中,因此只要一个虚表指针。

D=8-------->4(D虚表指针)+4(D虚表指针)

E=4--------->4(B虚表指针)

(二)、虚基类

1.什么是虚基类:

当在多条继承路径上有一个公共的基类,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明虚基类。所以可以说,虚基类是为了只实例化一次基类存在的。

2.虚基类的特点

(1):虚基类构造函数的参数必须由最新派生出来的类负责初始化(即使不是直接继承).
(2)虚基类的构造函数先于非虚基类的构造函数执行。

3.二义性的问题

1.问题所在:

代码展示:

#include <iostream>
using namespace std;
class Student
{public:int age;int number;Student(int a, int nu) :age(a), number(nu) {}Student() {}void set(){cout << "请输入学生的年龄和学号" << endl;cin >> age >> number;}void show(){cout << "学生的年龄为:" << " " << "学号为:" << endl;}
};
class Student1 :public Student
{void show(){cout << "Student1的" << endl; }
};
class Student2 :public Student{void show(){cout << "Student2的" << endl;}
};
class Student3 :public Student1,public Student2
{void show() {cout << "Student3的" << endl; }
};
int main()
{Student3 s3;s3.set();return 0;}

效果展示:

2.【二义性的解决非虚基类正确示范】

代码展示

#include <iostream>
using namespace std;
class Student
{public:int age;int number;Student(int a, int nu) :age(a), number(nu) {}Student() {}void set(){cout << "请输入学生的年龄和学号" << endl;cin >> age >> number;}void show(){cout << "学生的年龄为:" << " " << "学号为:" << endl;}
};
class Student1 :public Student
{public:void show(){cout << "Student1的" << endl;}
};
class Student2 :public Student
{void show(){cout << "Student2的" << endl; }
};
class Student3 :public Student1, public Student2
{void show(){cout << "Student3的" << endl; }
};
int main()
{Student3 s3;s3.Student1::set();return 0;
}

效果展示:

3.【二义性解决虚基类的方法正确示范】

#include <iostream>
using namespace std;
class Student
{public:int age;int number;Student(int a, int nu) :age(a), number(nu) {}Student() {}void set(){cout << "请输入学生的年龄和学号" << endl;cin >> age >> number;}void show(){cout << "学生的年龄为:" << " " << "学号为:" << endl;}
};
class Student1 :virtual public Student
{public:void show(){cout << "Student1的" << endl;}};
class Student2 :virtual public Student
{void show(){cout << "Student2的" << endl;}
};
class Student3 :public Student1, public Student2
{void show() {cout << "Student3的" << endl;}
};
int main()
{Student3 s3;s3.set();return 0;
}

代码展示


(三)、纯虚函数

1.纯虚函数的作用

在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。

2.基本格式:

class <类名>
{virtual <类型><函数名>(<参数表>)=0;…
};

(四)、总结

虚基类
1, 一个类可以在一个类族中既被用作虚基类,也被用作非虚基类。
2, 在派生类的对象中,同名的虚基类只产生一个虚基类子对象,而某个非虚基类产生各自的子对象。
3, 虚基类子对象是由最新派生类的构造函数通过调用虚基类的构造函数进行初始化的
4, 最派生类是指在继承结构中建立对象时所指定的类。
5, 派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用;如果未列出,则表示使用该虚基类的缺省构造函数。
6, 从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生 类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象 只初始化一次。
7, 在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行

虚函数
1, 虚函数是非静态的、非内联的成员函数,而不能是友元函数,但虚函数可以在另一个类中被声明为友元函数。
2, 虚函数声明只能出现在类定义的函数原型声明中,而不能在成员函数的函数体实现的时候声明。
3, 一个虚函数无论被公有继承多少次,它仍然保持其虚函数的特性。
4, 若类中一个成员函数被说明为虚函数,则该成员函数在派生类中可能有不同的实现。当使用该成员函数操作指针或引用所标识的对象时 ,对该成员函数调用可采用动态联编。
5, 定义了虚函数后,程序中声明的指向基类的指针就可以指向其派生类。在执行过程中,该函数可以不断改变它所指向的对象,调用不同 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。 纯虚函数 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。

纯虚函数
1, 当在基类中不能为虚函数给出一个有意义的实现时,可以将其声明为纯虚函数,其实现留待派生类完成。
2, 纯虚函数的作用是为派生类提供一个一致的接口。
3, 纯虚函数不能实列化

38.【C++ 虚函数 纯虚函数 虚基类 (最全详解)】相关推荐

  1. c++ 虚函数多态、纯虚函数、虚函数表指针、虚基类表指针详解

    文章目录 静态多态.动态多态 虚函数 哪些函数类型不可以被定义成虚函数? 虚函数的访问方式 析构函数中的虚函数 虚函数表指针 vptr 多继承下的虚函数表 虚基类表指针 bptr 纯虚函数 抽象类 虚 ...

  2. pandas新字段(数据列)生成、使用np.where或者apply lambda函数结合if else生成新的字段,详解及实战

    pandas新字段(数据列)生成.使用np.where或者apply lambda函数结合if else生成新的字段,详解及实战 pandas apply用法: pandas apply方法的作用原理 ...

  3. (38)System Verilog类class复制详解

    (38)System Verilog类class复制详解 1.1 目录 1)目录 2)FPGA简介 3)System Verilog简介 4)System Verilog类class复制详解 5)结语 ...

  4. Php开源h5相册系统,HTML5 CSS3专题 纯CSS打造相册效果的示例代码详解

    HTML5 CSS3专题 纯CSS打造相册效果的示例代码详解: 今天偶然发现电脑里面还有这样的一个例子,感觉效果还不错,不记得啥时候下载的了,也好久没上w3cfuns了,怀念学习前台的日子,给大家分享 ...

  5. python中可变参数*args传入函数时的存储方式为_python 中文读法详解Python函数可变参数定义及其参数传递方式...

    Python函数可变参数定义及其参数传递方式详解 python中 函数不定参数的定义形式如下 1. func(*args) 传入的参数为以元组形式存在args中,如: def func(*args): ...

  6. python process 函数_Python Process创建进程的2种方法详解

    前面介绍了使用 os.fork() 函数实现多进程编程,该方法最明显的缺陷就是不适用于 Windows 系统.本节将介绍一种支持 Python 在 Windows 平台上创建新进程的方法. Pytho ...

  7. linux getenv函数 get,linux之getenv putenv setenv和unsetenv详解

    1.getenv函数 头文件:#include 函数原型: char * getenv(const char* name); 函数说明:getenv()用来取得参数name环境变量的内容. 函数参数: ...

  8. python二维图颜色函数_通过python改变图片特定区域的颜色详解

    通过python改变图片特定区域的颜色详解 首先让我祭出一张数学王子高斯的照片,这位印在德国马克上的神人有多牛呢? 他是近代数学的奠基人之一,与牛顿, 阿基米德并称顶级三大数学家,随便找一个编程语言的 ...

  9. C语言getline函数CSDN,c++中的两种getline用法详解

    getline是C++标准库函数:但不是C标准库函数,而是POSIX(IEEE Std 1003.1-2008版本及以上)所定义的标准库函数(在POSIX IEEE Std 1003.1-2008标准 ...

最新文章

  1. 计算机网络基础 — 网络设备 — 以太交换机(Switch)
  2. 每天多一点(2016.12.04)》Javascript隐式转换
  3. 安卓没删干净导致报错
  4. WinForm的RadioButton使用小技巧
  5. 一封电子邮件的发送和接收的主要步骤
  6. Mysql数据库(二)——mysql数据库管理
  7. Linux下Bluez的编程实现
  8. 二级联动菜单ajax刷新,jquery json ajax 二级联动菜单实例
  9. 某些专题页面内容很好,但很长时间都不被及时收录的可能原因之一
  10. servlet ehcache beanfactroy c3p0 配置文件
  11. 【原创】不如预想的精彩—读《别闹了,费曼先生》
  12. 企业使用Windows Sysprep工具来封装Win10、Win11操作系统(最新最全)
  13. 优化js脚本设计,防止浏览器假死
  14. 盘点4种方法用Python批量提取[]括号内的第一个元素
  15. 推荐免费下载430套大型企业管理源码 下载地址:http://www.hur.cn/tg/linkin.asp?linkid=205389 下载地址:[URL=http://www.hur.cn/t
  16. PM血泪总结项目管理中存在的教训
  17. TA进阶实例34(Unreal制作水晶星光效果)
  18. 虚幻引擎 虚拟直播,实时渲染直播画面
  19. [Android驱动] 高通 Q PNP Flash LED驱动 及上层调用的流程
  20. 币氪研报|VET(VeChain)

热门文章

  1. 【题解】POJ 1852 Ants(搜索)
  2. 7.【kafka运维】 kafka-consumer-groups.sh消费者组管理
  3. 想知道路的尽头是什么与站在路的尽头回望
  4. 世界最强五百企业你都知道嘛 ?python带你走进它们的数据~
  5. hive 建表,分桶表(clustered by)、分桶且桶内排序(clustered by+sorted by)、分区表(partitioned by)、分区分桶一起用
  6. 电商CRM的痛点在哪里?
  7. 北京学习之旅的一些感受!
  8. ArrayList集合如何实现升序和降序
  9. 尚学堂oracle笔记2
  10. 夏威夷在国际豪华地产购买者的购房目的地中名列第一