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并不支持多继承。

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

目录
打赏
0
0
0
0
1
分享
相关文章
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
43 0
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
111 0
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
113 12
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
98 16
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
类和对象(上)(C++)
本篇内容主要讲解了C++中类的相关知识,包括类的定义、实例化及this指针的作用。详细说明了类的定义格式、成员函数默认为inline、访问限定符(public、protected、private)的使用规则,以及class与struct的区别。同时分析了类实例化的概念,对象大小的计算规则和内存对齐原则。最后介绍了this指针的工作机制,解释了成员函数如何通过隐含的this指针区分不同对象的数据。这些知识点帮助我们更好地理解C++中类的封装性和对象的实现原理。
|
4月前
|
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
235 6
类和对象(下)C++
本内容主要讲解C++中的初始化列表、类型转换、静态成员、友元、内部类、匿名对象及对象拷贝时的编译器优化。初始化列表用于成员变量定义初始化,尤其对引用、const及无默认构造函数的类类型变量至关重要。类型转换中,`explicit`可禁用隐式转换。静态成员属类而非对象,受访问限定符约束。内部类是独立类,可增强封装性。匿名对象生命周期短,常用于临时场景。编译器会优化对象拷贝以提高效率。最后,鼓励大家通过重复练习提升技能!
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问