多重继承之虚继承(主要是为了解决产生的数据冗余问题)

虚继承 是面向对象编程中的一种技术,是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给也从这个基类型直接或间接派生的其它类。形式:在继承定义中包含了virtual关键字的继承关系,如下图中,类A就叫做虚基类。
虚拟继承是多重继承中的菱形继承所特有的概念。虚拟基类是为解决多重继承而出现的。

菱形继承中既有多继承,如下图所示:

菱形继承中也有多重继承:

 现实中的例子:



问题来了:在类D的实例中将有两份类A的变量,这种数据冗余的现象我们不能容忍其存在?这就会需要用到虚继承!!!废话不多说直接上代码,直接粘贴可用。

#include <iostream>

#include <stdlib.h>
#include <string>
using namespace std;


/**
 * 定义人类: Person
 */
class Person
{
public:
    Person(string color = "blue"):m_strColor(color)
{
cout << "Person" << endl;
}
~Person()
{
cout << "~Person" << endl;
}
void eat()
{
        cout << m_strColor << endl;
        // cout << m_iAge << endl;
cout << "Person -- eat" << endl;
}
protected:
    string m_strColor;
};


/**
 * 定义工人类: Worker
 * 虚继承人类
 */
class Worker :  virtual public Person
{
public:
Worker(string name,string color):Person("Worker"+color)
{
m_strName = name;
cout << "Worker" << endl;
}
~Worker()
{
cout << "~Worker" << endl;
}
void work()
{
cout << m_strName << endl;
cout << "work" << endl;
}
protected:
string m_strName;
};


/**
 * 定义儿童类:Children
 * 虚继承人类
 */
class Children : virtual public Person
{
public:
Children(int age,string color):Person("Children"+ color)
{
m_iAge = age;
cout << "Children" << endl;
}
~Children()
{
cout << "~Children" << endl;
}
void play()
{
        cout<<
cout << m_iAge << endl;
cout << "play" << endl;
}
protected:
int m_iAge;
};


/**
 * 定义童工类:ChildLabourer
 * 公有继承工人类和儿童类
 */
class ChildLabourer:public Children,public Worker
{
public:
ChildLabourer(string name, int age,string color):Worker(name,color),Children(age,color)
{
cout << "ChildLabourer" << endl;
}


~ChildLabourer()
{
cout << "~ChildLabourer" << endl;
}
};


int main(void)
{
    // 用new关键字实例化童工类对象
ChildLabourer * p = new ChildLabourer("qq",14,"yellow");
    // 调用童工类对象各方法。
//   p->eat();
        p->Worker::eat();
        p->Children::eat();
p->work();
p->play();
delete p;
p = NULL;


return 0;

}

输出:

Person
Children
Worker
ChildLabourer
blue
Person -- eat
blue
Person -- eat
qq
work
0x60314814
play
~ChildLabourer
~Worker
~Children
~Person
如果不加virtual关键字时候的输出:

Person
Children
Person
Worker
ChildLabourer
Workeryellow
Person -- eat
Childrenyellow
Person -- eat
qq
work
0x60310814
play
~ChildLabourer
~Worker
~Person
~Children
~Person
分析:

(1)不加virtual情况:

不加virtual,也就是不是虚继承的情况下,在实例化童工这个类的时候,会按继承顺序,先调用类Children的构造函数再调用类Worker的构造函数,最后调用自己的构造函数,而调用Children和Worker的构造函数的时候又会分别先调用它们的基类Person的构造函数,这样就会生成两个Person的对象,从而生成两份Person所含有的数据成员,即童工类ChildLabourer在实例化的时会生成在内存中会生成两份Person的数据成员,所以在调用Children和Worker的eat()函数的时候,会分别打印出Workeryellow和Childrenyellow,(这里注意Children和Worker里面的eat()都是从Person继承来的,因此分别都会打印出Person --eat;还要注意调用方式:p->Worker::eat();p->Children::eat())

销毁对象时,会先调用自己的析构函数,再调用两个基类的析构函数,两个基类的析构函数调用之前都会先调用Person的析构函数。调用析构函数的顺序和调用构造函数相反。

(2)加virtual的情况:

加virtual以后,从输出结果可以看出,在实例化童工类ChildrenLabourer时,构造函数的调用顺序比较正常,只调用了一次Person,析构函数的调用也只会调用一次Person的析构函数,这说明实例化童工类时只会生成一份Person的对象,表明了在对象的内存空间中仅仅能够包含一份虚基类的对象,而且打印的结果都是blue,即ChildrenLabourer的数据不会再传入虚基类Person。这就讲数据冗余的问题解决了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385026.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

通过syslog接收远程日志

通过syslog接收远程日志通过syslog接收远程主机的日志&#xff0c;需要做一些环境配置。客户机A通过syslog将日志信息发送到服务主机B&#xff08;或称日志采集服务器&#xff09;。以下说明配置过程&#xff08;我的实验环境是&#xff0c;客户机A&#xff1a;Solaris 10&…

linux syslog服务器配置,自动发日志到另一台日志服务器

1.客户端:168.1.20.66修改/etc/syslog.conf 添加syslog.info 168.1.80.302.日志服务器:168.1.80.30修改/etc/sysconf/syslog 修改SYSLOGD_OPTIONS为 "-r -x -m 0" #-r表示允许接收外来的消息&#xff0c;-x表示不解析DNS, #-m 0表示时间戳标记间隔,如果指定只接…

Make文件(一)

基本规则&#xff1a; 目标&#xff1a;依赖 &#xff08;tab&#xff09;规则 目标&#xff1a;需要生成的目标文件 依赖&#xff1a;生成该目标所需的一些文件 规则&#xff1a;由依赖文件生成目标文件的手段 tab&#xff1a;每条规则前必须以tab开头&#xff0c;使用空格不行…

移植驱动完毕后加载时的version magic报错原因以及解决办法

History:2012-02-17Author:yingru移植rt3070的AP驱动到装有fedora14的PC机上时&#xff0c;模块编译完毕后&#xff0c;加载时提示invalid module format。PC机环境介绍&#xff1a;内核版本&#xff1a;2.6.35.6-45.fc14.i686命令行输入dmesg查看最后的日志&#xff0c;发现如…

/proc 虚拟文件系统(实例)

Linux下有一个神奇的目录/proc&#xff0c;经常会运行 cat /proc/cpuinfo 命令查看cpu信息&#xff0c;/proc下的确有cpuinfo文件&#xff0c;但是这个文件不是物理存在的&#xff0c;是软件虚拟出来的&#xff0c;与普通文件不同&#xff0c;该文件是动态的。通过/proc可以实现…

内核模块中对文件的读写

平时网络部分的东西碰的多些&#xff0c;这块一开始还真不知道怎么写&#xff0c;因为肯定和在用户空间下是不同的。google过后&#xff0c;得到以下答案。一般可以用两种方法&#xff1a;第一种是用系统调用。第二种方法是filp->open()等函数。下面分别来说下这两种方法。1…

Makefile文件试错

1成功&#xff1a; src $(wildcard ./*cpp) obj $(patsubst %.cpp,%.o ,$(src))target test$(target) : $(obj)g $(obj) -o $(target) -I/usr/include/mysql -L/usr/lib/mysql/ -lmysqlclient %.o: %.cppg -c $< -o $ -I/usr/include/mysql -L/usr/lib/mysql/ -lmysql…

内核定时器timer_list使用

Linux内核中提供了timer使用的API&#xff0c;做一个简单的记要。 1. 包含的头文件&#xff1a;linux/timer.h 2. 数据类型&#xff1a;struct timer_list; 包含的主要成员&#xff1a; a. data:传递到超时处理函数的参数&#xff0c;主要在多个定时器同时使用时&#xff0c;区…

内存四区

1.代码区&#xff1a; 代码区Code&#xff0c;程序被操作系统加载到内存的时候&#xff0c;所有的可执行代码都加载到代码区&#xff0c;也叫代码段&#xff0c;这块内存是不可以在运行期间修改的。 2. 静态区 所有的全局变量以及程序中的静态变量都存储在静态区。 #include<…

最高效的进(线)程间通信机制--eventfd

我们常用的进程&#xff08;线程&#xff09;间通信机制有管道&#xff0c;信号&#xff0c;消息队列&#xff0c;信号量&#xff0c;共享内存&#xff0c;socket等等&#xff0c;其中主要作为进程&#xff08;线程&#xff09;间通知/等待的有管道pipe和socketpair。线程还有特…

malloc,calloc,realloc

与堆操作相关的两个函数 malloc #include<stdio.h> #include<stdlib.h> #include<string.h>int main() {char *p malloc(10); //内存随机&#xff0c;未做处理int i;for(i 0; i < 10: i){printf(“%d “,p[i]);} free(p);return 0; } 运行结果&…

Linux内核同步机制之completion

内核编程中常见的一种模式是&#xff0c;在当前线程之外初始化某个活动&#xff0c;然后等待该活动的结束。这个活动可能是&#xff0c;创建一个新的内核线程或者新的用户空间进程、对一个已有进程的某个请求&#xff0c;或者某种类型的硬件动作&#xff0c;等等。在这种情况下…

可变参数函数(一)

一个函数可以接受不定数的参数个数&#xff0c;这就是可变参数函数&#xff0c;比较常见的比如printf(),scanf()&#xff1b; printf(const char* format,…); printf(“%d”,i); printf(“%s”,s); printf(“the number is %d,stirng is :%s”,i,s); 变量参数函数的简单实现&a…

Linux内核线程kernel thread详解--Linux进程的管理与调度

内核线程为什么需要内核线程Linux内核可以看作一个服务进程(管理软硬件资源&#xff0c;响应用户进程的种种合理以及不合理的请求)。 内核需要多个执行流并行&#xff0c;为了防止可能的阻塞&#xff0c;支持多线程是必要的。 内核线程就是内核的分身&#xff0c;一个分身可以处…

可变参数函数(二)

函数样例&#xff1a; #include<stdio.h> #include<stdlib.h> #include<stdarg.h>double add(int n,...) {int i 0;double sum 0;va_list argptr;va_start(argptr,n);for(i 0 ; i < n; i){double d va_arg(argptr,double);printf("%d argument …

Linux 内核网络协议栈 ------sk_buff 结构体 以及 完全解释 (2.6.16)

在2.6.24之后这个结构体有了较大的变化&#xff0c;此处先说一说2.6.16版本的sk_buff&#xff0c;以及解释一些问题。一、先直观的看一下这个结构体~~~~~~~~~~~~~~~~~~~~~~在下面解释每个字段的意义~~~~~~~~~~~[cpp] view plaincopyprint?struct sk_buff { /* These…

可变参数输出(三)

Linux C关于输出函数的定义&#xff1a; int printf(const char *format,…); int vprintf(const char * format,va_list ap); int vfprintf(FILE *stream,cosnt char *format,va_list ap); int vsprintf(char *str,const char *format,va_list ap); int vsnprintf(char *str,s…

最常用的设计模式---适配器模式(C++实现)

适配器模式属于结构型的设计模式&#xff0c;它是结构型设计模式之首&#xff08;用的最多的结构型设计模式&#xff09;。 适配器设计模式也并不复杂&#xff0c;适配器它是主要作用是将一个类的接口转换成客户希望的另外一个接口这样使得原本由于接口不兼容而不能一起工作的那…

Linux 简单打印日志(二)

#include<stdio.h> #include<stdlib.h> #include<string.h> #include<time.h> //#include<windows.h> #include <unistd.h> // linux下头文件#define FILE_MAX_SIZE (1024*1024)void get_local_time(char* buffer){time_t rawtime;struct …

程序随笔——C++实现的一个线程池

1.线程池简介 我们知道在线程池是一种多线程处理形式&#xff0c;处理过程中我们将相应的任务提交给线程池&#xff0c;线程池会分配对应的工作线程执行任务或存放在任务队列中&#xff0c;等待执行。 面向对象编程中&#xff0c;创建和销毁对象是需要消耗一定时间的&#xff0…