C++中的虚基类

简介: 🐰虚基类🌸虚基类的声明🌸虚基类的初始化🌸总结

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

🐰虚基类

🌸虚基类的声明

🌸虚基类的初始化

🌸总结


🐰虚基类

如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类成员的多份同名成员。这种情况有时也是有可能出现的,还增加了访问这些成员时的困难,容易出错。为了解决这个问题,可以使用虚基类的方法。

例如:

1. class Wood
2. {
3.     ...
4. };
5. class Sofa:virtual public Wood
6. {
7.     ...
8. };
9. class Bed:virtual public Wood
10. {
11.     ...
12. };

注意:虚基类并不是在声明基类时声明的,而是在声明派生类时的指定继承方式时声明的。因为一个基类可以在派生一个派生类时作为虚基类,而在派生另一个派生类时不作虚基类。

🌸虚基类的声明

声明虚基类的形式:

class 派生类名:virtual 继承方式 基类名

就是在声名派生类时,将关键字virtual加在继承方式的前面

注意:虚基类这种方法方法只影响从指定了虚基类的派生类中进一步派生出的类,它不会影响派生类本身。为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中都把基类声明为虚基类。

🌸虚基类的初始化

如果虚基类中定义了带参数的构造函数,而且没有定义默认的构造函数,则在其所有的派生类(包括直接派生和间接派生的派生类)中,都要通过构造函数的初始化列表进行初始化。

虚基类的初始化。

例如:

person的数据成员有name, age,sex,add,phone,person派生了两个派生类Teaher和Cadre,Teacher新增了一个受保护的数据成员position,Cadre新增了一个受保护的数据成员post,类 TeacherCadre公有继承了Teaher和Cadre,由于Teaher和Cadre公有继承了person,有同名的成员,所以这里我们需要使用虚基类来处理,处理方法如下

1. #include<iostream>
2. using namespace std;
3. class person
4. {
5. public:
6. person(string Name,int Age,string Sex,string Add,string Phone)
7.     {
8.         name=Name;
9.         age=Age;
10.         sex=Sex;
11.         add=Add;
12.         phone=Phone;
13.     }
14. public:
15.     string name;
16. int age;
17.     string sex;
18.     string add;
19.     string phone;
20. };
21. class Teacher:virtual public person//虚基类
22. {
23. public:
24. Teacher(string name,int age,string sex,string add,string phone,string Position):person(name,age,sex,add,phone)
25.     {
26.         position=Position;
27.     }
28. void display()
29.     {
30.         cout<<"         姓名:"<<name<<endl;
31.         cout<<"         年龄:"<<age<<endl;
32.         cout<<"         地址:"<<add<<endl;
33.         cout<<"         电话:"<<phone<<endl;
34.     }
35. protected:
36.     string position;
37. };
38. class Cadre:virtual public person//虚基类
39. {
40. public:
41. Cadre(string name,int age,string sex,string add,string phone,string Post):person(name,age,sex,add,phone)
42.     {
43.         post=Post;
44.     }
45. protected:
46.     string post;
47. };
48. class TeacherCadre:public Teacher,public Cadre
49. {
50. public:
51. TeacherCadre(string name,int age,string sex,string add,string phone,string Position,string Post,double Wage):person(name,age,sex,add,phone),Teacher(name,age,sex,add,phone,Position),Cadre(name,age,sex,add,phone,Post)
52.                  {
53.                      wage=Wage;
54.                  }
55. void show()
56.     {
57.         Teacher::display();
58.         cout<<"         职务:"<<post<<endl;
59.         cout<<"         工资:"<<wage<<endl;
60.     }
61. protected:
62. double wage;
63. };
64. int main()
65. {
66. TeacherCadre t1("张三",32,"男","北京","1878****454","教导主任","老师",7856.34);
67.     t1.show();
68. }
69. 结果:
70. 姓名:张三
71. 年龄:32
72. 地址:北京
73. 电话:1878****454
74. 职务:老师
75. 工资:7856.34

或许大家会有疑问:类TeacherCadre的构造函数通过初始化列表调用了基类(person)的构造函数,而类Teacher和Cadre的构造函数也通过初始化列表调用了虚基类(person)的构造函数,这样虚基类的构造函数岂不是被调用了3次?大家不必担心,C++编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略其他派生类(Teacher和Cadre)对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化。

例如:我们把虚基类的构造函数改为

1. person(string Name,int Age,string Sex,string Add,string Phone)
2.     {
3.         name=Name;
4.         age=Age;
5.         sex=Sex;
6.         add=Add;
7.         phone=Phone;
8.         cout<<"秋水共长天一色,落霞与孤鹜齐飞"<<endl;
9.     }
10. 看一下运行结果:
11. 秋水共长天一色,落霞与孤鹜齐飞
12.          姓名:张三
13.          年龄:32
14.          地址:北京
15.          电话:1878****454
16.          职务:老师
17.          工资:7856.34

所以虚基类的构造函数真的只调用了一次,那就是最后一个的派生类调用了虚基类的构造函数

🌸总结

派生类构造函数调用的次序有如下的3个原则

(1)同一层中对虚基类构造函数的调用优先于对非基类构造函数的调用

(2)若同一层次中包含多个虚基类,则这些虚基类的构造函数按照它们在继承方式中的声明次序调用。

(3)若虚基类由非基类派生出来,则仍然调用基类的构造函数,在按派生类中构造函数的执行顺序调用

派生类的析构函数调用次序与构造函数恰恰相反。

最后一点,使用继承的时候一定注意二义性的问题,其实能够用单继承解决的就用单继承,因为多继承容易出问题。在面向对向对象的程序设计语言中java,Smalltalk并不支持多继承。

🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸  

相关文章
|
26天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
42 2
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
84 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
81 4
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
89 4
|
2月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
31 4
|
2月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
32 4
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
26 1
|
2月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
2月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
2月前
|
存储 编译器 C语言
【C++类和对象(上)】—— 我与C++的不解之缘(三)
【C++类和对象(上)】—— 我与C++的不解之缘(三)