当前位置: 首页 > news >正文

C++ 多继承

多继承(多重继承)

在前面的类继承例子中,基类虽然有多个派生类,但派生类都只有一个基类,称为单继承(Single inheritance)。除此之外,C++也支持多继承(Mutiple inheritance),即一个派生类可以派生自两个以上的多个基类

需要注意的是:多继承容易让代码逻辑复杂,思路混乱,中小型项目中较少使用


要深入多继承的概念,首先重温一下 继承的三种方式:

公有继承(public),私有继承(private),保护继承(protected)
三种继承方式的说明,如下表所示:
在这里插入图片描述


类多继承的语法:

多继承的语法很简单, 将多个基类用逗号隔开即可,例如已声明了类A,类B和类C,且ABC这三个类都当作基类一起派生一个派生类D,那么可以这样来声明派生类D:

class D:public A,protected B,private C //基类出现的顺序为 A - B - C
{
//派生类D的新增成员
};

以上述代码为例,D是多继承形式的派生类,D类以公有的方式继承A类,以保护的方式继承 B 类,以私有的方式继承 C 类,D类根据不同的继承方式来获取ABC三个基类的成员,确定ABC三个基类的成员们在派生类中的访问权限。


类多继承下的类构造函数:

多继承形式下的构造函数和单继承形式基本一致,只要在派生类的构造函数中调用多个基类的构造函数即可,以上述案例的A,B,C,D类为例,派生类D类的构造函数的写法为:

D::D(D构造器的参数列表):A(A构造器的参数列表),B(B构造器的参数列表),C(C构造器的参数列表)
{
	D构造器的其他操作
}

注意:🎯
基类们(ABC)的构造函数的在多继承时派生类构造函数中的调用顺序和它们在派生类构造函数中出现的顺序无关,而是取决与声明派生类时,各个基类出现的顺序(继承基类时出现的顺序为A-B-C),即使将D类构造函数写做下面的形式:

D::D(D构造器的参数列表):C(C构造器的参数列表)A(A构造器的参数列表),B(B构造器的参数列表)
{
	D构造器的其他操作
}

程序在运行时也是先调用A的构造函数,再调用B类的构造函数,最后再调用C类的构造函数


类多继承情况下的构造函数和析构函数

class D:public A,protected B,private C //基类出现的顺序为 A - B - C
{
//派生类D的新增成员
};

以上述代码为例 程序调用各类的构造函数的顺序为 A-B-C-D,而调用析构函数的顺序为D-C-B-A。这说明多继承形式下析构函数的执行顺序和构造函数的执行顺序是相反的


类多继承导致的类命名冲突

当两个或多个基类中有同名成员(例如A,B,C三个基类都有成员函数show())的时候,如果直接访问该成员(派生类D类中调用show()函数),就会产生命名冲突,编译器会进行不明确提示,因为编译器此时不知道该使用哪个基类的成员,这时需要在成员名字的前面加 类名和域解析符::,便可以 显式的指明到底使用哪个类的成员,以此消除类成员命名冲突导致的命名二义性。

例如以下代码:

//基类A
class BaseA
{
public:
    void show();
};
void BaseA::show()
{
    cout<<"基类A的show"<<m_a<<endl;
}

//基类B
class BaseB
{
public:
    void show();
};
void BaseB::show()
{
    cout<<"基类B的show"<<m_a<<endl;
}

//派生类Derived
class Derived: public BaseA, public BaseB
{
public:
    void display();
};
void Derived::display()
{
    BaseA::show();  //调用BaseA类的show()函数
    BaseB::show();  //调用BaseB类的show()函数
    cout<<"派生类Derived的display"<<m_a<<endl;
}

int main()
{
    Derived obj;
    obj.display();
    结果为:
    "基类A的show"
	"基类B的show"
    "派生类Derived的display"
    return 0;
}

多继承的实际应用

什么时候用到多继承?
只要遇到的问题无法只用一个"是一个"关系来描述的时候,就是多继承出场的时候

举个例子:
学校里有老师和学生,他们属于person类
从面向对象编程的角度上看,应该创建一个名为person的基类和两个名为Teacher()和Student的子类,后两者都是从
前者继承而来的

但问题来了,有些学生在上课同时还当助教赚钱该怎么办?此时就存在一种即是老师也是学生的复杂关系:也就是存在着两个"是一个"关系

我们此时就需要写一个TeachingStudent类让它同时继承Teacher类和Student类,换句话说,就是要使用多继承来解决两个是一个的问题。

代码演示

#include <iostream>
#include <string>

using namespace std;

class Person  //todo基类Person 由Teacher类和Student类共同继承
{
public:
	Person(string theName);
	void introduce();//介绍信息程序
protected:
	string name;
};

class Teacher :public Person  //todo既是派生类又是基类的Teacher类  继承Person类,派生TeachingStudent类
{
public:
	Teacher(string theName, string theClass);
	void teach();//老师授课程序
	void introduce();//介绍信息程序
protected:
	string classes;
};

class Student :public Person //todo既是派生类又是基类的Student类  继承Person类,派生TeachingStudent类
{
public:
	Student(string theName, string theClass);

	void attendClass();//学生上课程序
	void introduce();//介绍信息程序 
protected:
	string classes;
};

class TeachingStudent :public Student, public Teacher //todo派生类TeachingStudent类 基类为Student和teacher类
{
public:
	TeachingStudent(string theName, string classTeaching, string classAttending);
	void introduce();//介绍信息程序
};

Person::Person(string theName) //todo基类Person类构造函数
{
	name = theName;
}

void Person::introduce()//todo基类Person类介绍程序Person::introduce()
{
	cout << "大家好,我是" << name << "。\n\n";
}


//-----------------------------------------------------------------------------------------------------

//todo既是派生类又是基类的Teacher类 的构造函数 继承Person类的参数列表
Teacher::Teacher(string theName, string theClass) :Person(theName) 
{
	classes = theClass;
}
//todo既是派生类又是基类的Teacher类 的老师授课程序 Teacher::teach()
void Teacher::teach()
{
	cout << name << "教" << classes << "\n\n";
}
//todo既是派生类又是基类的Teacher类 的老师介绍程序  Teacher::introduce()
void Teacher::introduce()
{
	cout << "大家好,我是" << name << ",我教" << classes << "\n\n";
}

//-----------------------------------------------------------------------------------------------------

//todo既是派生类又是基类的Student类 的构造函数 继承Person类的参数列表
Student::Student(string theName, string theClass) :Person(theName)
{
	classes = theClass;
}
//todo既是派生类又是基类的Student类 的学生上课程序  Student::attendClass()
void Student::attendClass()
{
	cout << name << "加入" << classes << "学习\n\n";
}
//todo既是派生类又是基类的Student类 的学生介绍程序  Student::introduce()
void Student::introduce()
{	
	cout << "大家好,我是" << name << "我在" << classes << "学习\n\n";
}


//-----------------------------------------------------------------------------------------------------

//todo派生类TeachingStudent的构造函数 继承基类Teacher和Student的构造函数列表
TeachingStudent::TeachingStudent(string theName, string classTeaching, string classAttending) :Teacher(theName, classTeaching), Student(theName, classAttending)
{}


//todo派生类TeachingStudent的介绍程序  TeachingStudent::introduce()
void TeachingStudent::introduce()
{	
	cout << "大家好,我是" << Student::name << "我在教" << Teacher::classes << ",";
	cout << "同时我在" << Student::classes << "学习\n\n";
}

int main()
{
	Teacher teacher("张老师", "奥数班");
	Student student("小明", "奥数班");
	TeachingStudent teacherstudent("杰克", "英语班", "奥数班");

	teacher.introduce();
	teacher.teach();
	student.introduce();
	student.attendClass();
	teacherstudent.introduce();
	teacherstudent.teach();
	teacherstudent.attendClass();
	return 0;
}

运行结果:
在这里插入图片描述

相关文章:

  • redmine 主题thems-默认主题
  • C++ 虚继承
  • C++ 错误处理和调试(编写代码前的准备工作)
  • 将整型数字转换为大写汉字的自定义函数,如转换为'壹贰
  • C++ assert函数与捕获异常
  • WCF开发日志 -- OEA里面的WCF设计
  • C++ 内存分配
  • C++ 动态内存管理
  • IOS学习资源
  • C++ 从函数或方法返回动态内存(函数指针与指针函数)
  • PHP实现多web服务器共享SESSION数据-session数据写入mysql数据库
  • C++ 副本构造器
  • C++常识之——C++中堆和栈的区别,自由存储区、全局/静态存储区和常量存储区...
  • C++ 高级强制类型转换
  • thinkphp的一些类库
  • 【MySQL经典案例分析】 Waiting for table metadata lock
  • CSS魔法堂:Absolute Positioning就这个样
  • Go 语言编译器的 //go: 详解
  • hadoop入门学习教程--DKHadoop完整安装步骤
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • OSS Web直传 (文件图片)
  • 今年的LC3大会没了?
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 由插件封装引出的一丢丢思考
  • 仓管云——企业云erp功能有哪些?
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • ​软考-高级-信息系统项目管理师教程 第四版【第23章-组织通用管理-思维导图】​
  • #快捷键# 大学四年我常用的软件快捷键大全,教你成为电脑高手!!
  • #微信小程序:微信小程序常见的配置传旨
  • (初研) Sentence-embedding fine-tune notebook
  • (分布式缓存)Redis持久化
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (力扣记录)1448. 统计二叉树中好节点的数目
  • (十)T检验-第一部分
  • (算法)Travel Information Center
  • (五)IO流之ByteArrayInput/OutputStream
  • (一)基于IDEA的JAVA基础12
  • ./configure,make,make install的作用
  • .[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复
  • .NET 3.0 Framework已经被添加到WindowUpdate
  • .Net 8.0 新的变化
  • .NET Micro Framework初体验
  • .net MySql
  • .NET Remoting Basic(10)-创建不同宿主的客户端与服务器端
  • .NET 事件模型教程(二)
  • .net 中viewstate的原理和使用
  • .net 逐行读取大文本文件_如何使用 Java 灵活读取 Excel 内容 ?
  • .NET/C# 使用反射调用含 ref 或 out 参数的方法
  • .NET分布式缓存Memcached从入门到实战
  • .net利用SQLBulkCopy进行数据库之间的大批量数据传递
  • ::
  • ??在JSP中,java和JavaScript如何交互?
  • [ vulhub漏洞复现篇 ] Django SQL注入漏洞复现 CVE-2021-35042
  • []串口通信 零星笔记
  • [2]十道算法题【Java实现】