什么是继承???概念及作用(好处)?谢谢高手指点

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是继承???概念及作用(好处)?谢谢高手指点相关的知识,希望对你有一定的参考价值。

Java中的继承??波

继承 百科内容来自于: 继承是指一个对象直接使用另一对象的属性和方法。也指按照法律或遵照遗嘱接受死者的财产、职务、头衔、地位等。
继承

正文
对于死者生前财产的权利和义务的承受。继承法即关于自然人死后由其继承人对其财产权利和义务予以承受的法律规范的总称。

民法中的继承 民法中的继承是一种法律制度,即指将死者生前的财产和其他合法权益转归有权取得该项财产的人所有的法律制度。
继承人依照法律规定承受被继承人遗产的权利,称为继承权,继承权具有下列法律特征:①是一种财产权利,通过继承实现财产的移转。②以人身关系为基础。世界各国有关法定继承的规定,都是以继承人和被继承人存在婚姻、血缘等关系为依据而确定的。③继承权的实现要有一定的法律事实。法律规定的继承权只是继承人享有的一种期待权;只有被继承人死亡这一法律事实出现以后,继承权才成为既得权,开始遗产继承。在实行遗嘱继承制的国家,还需要有被继承人设立遗嘱的法律事实。
继承制度的沿革 继承制度是同特定的社会制度相联系的,是在社会出现私有制、分裂为阶级以后随同国家的产生而产生的。列宁说:“遗产制度以私有制为前提”(《列宁选集》第1卷,第20页)。早在母系氏族社会,氏族中的财产,如土地、住房、家畜和其他生产资料,都属于公有,个人财产只限于少量生活用品和随身携带的武器。氏族首领死后,财产中的日用品随葬。其余财物由同族人共有。这时的继承,只是一种社会习惯,还未形成法律制度。进入阶级社会以后,法律首先确认身份继承,而后确认独立的财产继承。
奴隶社会的继承 随着社会生产力的发展,在生产中逐渐形成了以男性为中心的社会,母权制随之为父权制所取代。其主要标志之一,就是按男性计算世系和按父系进行继承。父亲死后,由其子继承其身份,并继承其财产。奴隶制社会形成了部落酋长的世袭制和财产继承制,并用法律形式固定下来。如公元前18世纪的《汉穆拉比法典》有关继承权的规定达23条,内容比较详尽。中国的《周礼》、《礼记》中,也有对西周奴隶制时期婚姻继承的记载,其核心是嫡长子继承制。这种制度的经济基础是奴隶占有制及大家族所有权,国王和各级贵族就是奴隶制大家族的族长,一族的权力集于族长一身,这种身份就是各种权利包括财产权的依据。所以,从历史上看,最早出现的继承是身份继承。族长死后,就由其嫡长子继承其统治身份和政治地位,尤其是继承宗庙祭祀权,随之掌握财产。身份继承是财产继承的前提。
封建社会的继承 国王和大小贵族是土地所有者,也是各级统治者。他们从血缘关系、宗族关系出发,构成宝塔式的阶级统治体系。为了保护私有财产不被分散,使之世代相承,国王和贵族死后,其权位、土地以及其他财产,由其嫡长子继承。这种以嫡长子继承为中心的继承制度,称为宗祧继承。宗祧继承是宗法社会制度的产物。以嫡长子为主要继承人,有子立长,无子立嗣,成为历代的法例。次子、庶子只能分得部分土地和财物,不能承袭权位。《唐律疏议》·户婚律规定:“诸立嫡违法者,徒一年。”这种法律制度,影响到日本、朝鲜和越南诸邻国。在日本旧民法中的“家督相续”,就是由嫡长子继承家长权和主要财产权。按照宗祧继承制,遗产的承受是以宗祧继承为前提的,只有有宗祧继承权者,始有财产继承权。而且立长、立嗣都是以男性为限,否认女性的继承权,明显地反映出男尊女卑的封建特点。
资本主义社会的继承 到了资本主义社会,财产继承制才独立起来,并最终取代了身份继承制,反映出继承关系同其他法律关系一样,是由经济基础所决定又为产生它的经济基础服务的一种社会意志关系。马克思说:“继承法最清楚地说明了法对于生产关系的依存性”(《马克思恩格斯全集》第3卷,第420页)。在资本主义经济基础上产生的继承制度,是为资本主义制度服务,保护资产阶级的既得利益的。1804年《法国民法典》设有专编规定取得财产的各种方法,其中的第1章就是继承。资本主义社会实行契约自由的原则,把遗嘱继承提到了重要地位,尤其在英美法系诸国,实行遗嘱自由,立遗嘱人在遗嘱中可以任意指定遗产继承人,以保证其财产得以传给自己中意的继承人,让他们继承攫取他人的劳动成果的权利。
苏联的继承 俄国十月革命后,曾于1918年4月24日宣布废除继承权,财产所有人死后其遗产均归国有,后又规定不超过一万卢布的遗产可由死者一定的近亲属继承。1922年《俄罗斯联邦民法典》规定了继承人范围和遗产继承的最高限额。1928年废除了关于 1万卢布的最高限额的规定。从1945年开始,遗产可以通过遗嘱继承。1961年《苏联和各加盟共和国民事立法纲要》,对于法定继承和遗嘱继承等主要问题,又进一步作出规定。
中国继承的发展 中国历代律例虽有继承方面的规定,但作为独立的继承法,则是光绪三十三年(1907)开始起草的,于宣统三年(1911)完成,列为《大清民律(草案)》的第5编,仍以宗祧继承为主。中华民国时期国民党政府因袭德、法、日民法典的体例,把继承法列在亲属编之后,从法律上废弃了宗祧继承,采取财产继承制。
中华人民共和国的继承制度是在社会主义经济基础上产生的法律制度。中国1982年宪法规定:“国家依照法律规定保护公民的私有财产的继承权。”在中国,公民的继承权主要是在社会主义生产资料公有制下,从按劳分配原则派生出来的,受到国家的保护。由于消灭了剥削阶级和剥削制度,主要生产资料为国家所有或者集体所有,从而使社会主义继承关系同过去的一切继承关系,有着本质的不同:一是以生产资料公有制为基础;一是继承关系的主体主要是劳动人民,继承关系的客体主要是生活资料,继承的目的不是剥削权利的延续。中国的社会主义继承制,不仅彻底废除了延续几千年的宗祧继承、立嗣承宗的制度,而且有利于妥善处理社会主义家庭中的财产关系,贯彻男女平等原则,保护无劳动能力和无生活来源的家庭成员的合法权益,以实现抚育子女、赡养老人的家庭职能。
关于继承的具体规定 当代各国,对继承问题都以民法或有关继承的单行法规作出各种具体规定。包括继承开始时间、地点;继承权的接受和放弃;继承权的丧失等。
继承开始 被继承人死亡或宣告死亡(见失踪和死亡宣告)的时间是继承开始时间,从此继承权开始具有实行的效力。在继承开始时,确定继承人的范围,确定继承遗产的范围和价值。如果是遗嘱继承,需要在这时认定遗嘱是否有效。关于继承权的时效也从此时开始计算。
继承地点通常是被继承人的住所地。如果住所不明或主要财产不在住所地,可以财产所在地为继承地点。例如日本现行民法规定,继承在被继承人的住所开始,如果住所地不明或主要财产不在住所地,则以财产所在地为继承开始地点。苏联也规定继承发生的地点是被继承人的最后经常住所,如最后经常住所不明,则为遗产所在地或遗产基本部分所在地。
继承的接受和放弃 享有继承权的人在得知被继承人死亡或宣告死亡后,应就是否接受继承作出意思表示。继承人接受或放弃继承的意思表示,是单方行为,具有法律效力。各国对此大体有两种规定:①法国、日本、苏联等国的民法规定,无论接受或放弃继承,都必须作出意思表示。②民主德国、匈牙利等国的民法规定,放弃继承必须作出意思表示,否则即视为接受继承。
继承人作出意思表示的期限,各国一般规定为6个月。但现行《法国民法典》第789条规定:“接受或放弃继承的权利,经过法律为不动产物权所规定的最长的时效期间而消灭”。这种时效期间据该法典第2262条规定为30年。
根据中国司法实践,继承人放弃继承权的,应在继承开始后至遗产处理前,作出放弃继承的表示。没有表示的,视为接受继承。继承人的继承权,从知道权利被侵害时起2年内可以请求人民法院予以保护。
继承权的丧失 继承人的继承权,除因作放弃继承的意思表示外,还可因被剥夺而丧失。剥夺继承权必须严格按照法律规定的条件,并按司法程序进行。
各国对剥夺继承权的条件都作了规定。日本民法规定:凡故意使被继承人、或继承顺序(见法定继承)在先或同等顺序的继承人致死的,或者因为想达到这个目的而被判刑的,知道被继承人被杀害而不告发的,以及用欺骗、强迫方法妨碍或促使被继承人作出遗嘱、取消遗嘱和变更遗嘱的,伪造、毁弃、隐匿遗嘱的,都不能成为继承人。又如1964年的《俄罗斯联邦民法典》则规定,只要公民以自己的违法行为反对被继承人或某一继承人,反对遗嘱的实现,以促使自己继承的,就无权继承;还规定剥夺亲权的父母,以及恶意拒绝扶养义务的人,也不得继承。其他各国也都按照本国的具体情况作了规定。继承权被剥夺后,该继承人即丧失了承受遗产的权利。但一些国家规定,已得到死者或行为所针对的人的宽恕,可不被剥夺继承权。
各国还规定被继承人可用遗嘱方式取消继承人的继承权,同时规定了一些限制条件。苏联规定,遗嘱人可在遗嘱中剥夺一个、几个或所有法定继承人的继承权,但其未成年子女、无劳动能力的子女(包括养子女)、无劳动能力的配偶、父母(包括养父母)和依靠遗嘱人生活的人,不论遗嘱内容如何,都得继承不少于依法继承时他们每人应继份额的三分之二。《法国民法典》经1972年修改后的条文规定:遗嘱处分的财产,遗有一个子女时不得超过半数,二个子女时不得超过三分之一,三个以上时不得超过四分之一,并作了出其他限制性规定。
根据中国司法实践,一般认为继承人有下列情形之一,在继承开始后,经司法程序剥夺其继承权:谋害被继承人或为争夺遗产而谋害其他继承人的;对被继承人生前有虐待、遗弃行为情节严重的;胁迫、欺骗被继承人设立遗嘱,或者伪造、篡改遗嘱的。在实行遗嘱继承时,一般认为,遗嘱中必须保留继承人中缺乏劳动能力和生活来源的人以及胎儿应得的遗产份额。
参考技术A 简单点说继承就是子类可以拥有父类的属性和方法,但父类没有子类的属性和方法。本回答被提问者采纳

C++继承

继承的概念及定义

继承的概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称为派生类。
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,而继承便是类设计层次的复用。

例如,以下代码中Student类和Teacher类就继承了Person类。

//父类
class Person
{
public:
	void Print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "张三"; //姓名
	int _age = 18;     //年龄
};
//子类
class Student : public Person
{
protected:
	int _stuid;   //学号
};
//子类
class Teacher : public Person
{
protected:
	int _jobid;   //工号
};


继承后,父类Person的成员,包括成员函数和成员变量,都会变成子类的一部分,也就是说,子类Student和Teacher复用了父类Person的成员。

继承的定义

定义格式

继承的定义格式如下:

说明: 在继承当中,父类也称为基类,子类是由基类派生而来的,所以子类又称为派生类。

继承方式和访问限定符

我们知道,访问限定符有以下三种:

  1. public访问
  2. protected访问
  3. private访问

而继承的方式也有类似的三种:

  1. public继承
  2. protected继承
  3. private继承

继承基类成员访问方式的变化

基类当中被不同访问限定符修饰的成员,以不同的继承方式继承到派生类当中后,该成员最终在派生类当中的访问方式将会发生变化。

类成员/继承方式public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected成员派生类的private成员
基类的protected成员派生类的protected成员派生类的protected成员派生类的private成员
基类的private成员在派生类中不可见在派生类中不可见在派生类中不可见

稍作观察,实际上基类成员访问方式的变化规则也不是无迹可寻的,我们可以认为三种访问限定符的权限大小为:public > protected > private,基类成员访问方式的变化规则如下:

  1. 在基类当中的访问方式为public或protected的成员,在派生类当中的访问方式变为:Min(成员在基类的访问方式,继承方式)。
  2. 在基类当中的访问方式为private的成员,在派生类当中都是不可见的。

基类的private成员在派生类当中不可见是什么意思?

这句话的意思是,我们无法在派生类当中访问基类的private成员。例如,虽然Student类继承了Person类,但是我们无法在Student类当中访问Person类当中的private成员_name。

//基类
class Person
{
private:
	string _name = "张三"; //姓名
};
//派生类
class Student : public Person
{
public:
	void Print()
	{
		//在派生类当中访问基类的private成员,error!
		cout << _name << endl; 
	}
protected:
	int _stuid;   //学号
};

也就是说,基类的private成员无论以什么方式继承,在派生类中都是不可见的,这里的不可见是指基类的私有成员虽然被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。

因此,基类的private成员在派生类中是不能被访问的,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就需要定义为protected,由此可以看出,protected限定符是因继承才出现的。

注意: 在实际运用中一般使用的都是public继承,几乎很少使用protected和private继承,也不提倡使用protected和private继承,因为使用protected和private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。

默认继承方式

在使用继承的时候也可以不指定继承方式,使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public。

例如,在关键字为class的派生类当中,所继承的基类成员_name的访问方式变为private。

//基类
class Person
{
public:
	string _name = "张三"; //姓名
};
//派生类
class Student : Person //默认为private继承
{
protected:
	int _stuid;   //学号
};

而在关键字为struct的派生类当中,所继承的基类成员_name的访问方式仍为public。

//基类
class Person
{
public:
	string _name = "张三"; //姓名
};
//派生类
struct Student : Person //默认为public继承
{
protected:
	int _stuid;   //学号
};

注意: 虽然继承时可以不指定继承方式而采用默认的继承方式,但还是最好显示的写出继承方式。

基类和派生类对象赋值转换

派生类对象可以赋值给基类的对象、基类的指针以及基类的引用,因为在这个过程中,会发生基类和派生类对象之间的赋值转换。

例如,对于以下基类及其派生类。

//基类
class Person
{
protected:
	string _name; //姓名
	string _sex;  //性别
	int _age;     //年龄
};
//派生类
class Student : public Person
{
protected:
	int _stuid;   //学号
};

代码当中可以出现以下逻辑:

Student s;
Person p = s;     //派生类对象赋值给基类对象
Person* ptr = &s; //派生类对象赋值给基类指针
Person& ref = s;  //派生类对象赋值给基类引用

对于这种做法,有个形象的说法叫做切片/切割,寓意把派生类中基类那部分切来赋值过去。
派生类对象赋值给基类对象图示:

派生类对象赋值给基类指针图示:

派生类对象赋值给基类引用图示:

注意: 基类对象不能赋值给派生类对象,基类的指针可以通过强制类型转换赋值给派生类的指针,但是此时基类的指针必须是指向派生类的对象才是安全的。

继承中的作用域

在继承体系中的基类和派生类都有独立的作用域。若子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。

例如,对于以下代码,访问成员_num时将访问到子类当中的_num。

#include <iostream>
#include <string>
using namespace std;
//父类
class Person
{
protected:
	int _num = 111;
};
//子类
class Student : public Person
{
public:
	void fun()
	{
		cout << _num << endl;
	}
protected:
	int _num = 999;
};
int main()
{
	Student s;
	s.fun(); //999
	return 0;
}

若此时我们就是要访问父类当中的_num成员,我们可以使用作用域限定符进行指定访问。

void fun()
{
	cout << Person::_num << endl; //指定访问父类当中的_num成员
}

需要注意的是,如果是成员函数的隐藏,只需要函数名相同就构成隐藏。

例如,对于以下代码,调用成员函数fun时将直接调用子类当中的fun,若想调用父类当中的fun,则需使用作用域限定符指定类域。

#include <iostream>
#include <string>
using namespace std;
//父类
class Person
{
public:
	void fun(int x)
	{
		cout << x << endl;
	}
};
//子类
class Student : public Person
{
public:
	void fun(double x)
	{
		cout << x << endl;
	}
};
int main()
{
	Student s;
	s.fun(3.14);       //直接调用子类当中的成员函数fun
	s.Person::fun(20); //指定调用父类当中的成员函数fun
	return 0;
}

特别注意: 代码当中,父类中的fun和子类中的fun不是构成函数重载,因为函数重载要求两个函数在同一作用域,而此时这两个fun函数并不在同一作用域。为了避免类似问题,实际在继承体系当中最好不要定义同名的成员。

派生类的默认成员函数

默认成员函数,即我们不写编译器会自动生成的函数,类当中的默认成员函数有以下六个:

下面我们看看派生类当中的默认成员函数,与普通类的默认成员函数的不同之处。

例如,我们以下面这个Person类为基类。

//基类
class Person
{
public:
	//构造函数
	Person(const string& name = "peter")
		:_name(name)
	{
		cout << "Person()" << endl;
	}
	//拷贝构造函数
	Person(const Person& p)
		:_name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}
	//赋值运算符重载函数
	Person& operator=(const Person& p)
	{
		cout << "Person& operator=(const Person& p)" << endl;
		if (this != &p)
		{
			_name = p._name;
		}
		return *this;
	}
	//析构函数
	~Person()
	{
		cout << "~Person()" << endl;
	}
private:
	string _name; //姓名
};

我们用该基类派生出Student类,Student类当中的默认成员函数的基本逻辑如下:

//派生类
class Student : public Person
{
public:
	//构造函数
	Student(const string& name, int id)
		:Person(name) //调用基类的构造函数初始化基类的那一部分成员
		, _id(id) //初始化派生类的成员
	{
		cout << "Student()" << endl;
	}
	//拷贝构造函数
	Student(const Student& s)
		:Person(s) //调用基类的拷贝构造函数完成基类成员的拷贝构造
		, _id(s._id) //拷贝构造派生类的成员
	{
		cout << "Student(const Student& s)" << endl;
	}
	//赋值运算符重载函数
	Student& operator=(const Student& s)
	{
		cout << "Student& operator=(const Student& s)" << endl;
		if (this != &s)
		{
			Person::operator=(s); //调用基类的operator=完成基类成员的赋值
			_id = s._id; //完成派生类成员的赋值
		}
		return *this;
	}
	//析构函数
	~Student()
	{
		cout << "~Student()" << endl;
		//派生类的析构函数会在被调用完成后自动调用基类的析构函数
	}
private:
	int _id; //学号
};

派生类与普通类的默认成员函数的不同之处概括为以下几点:

  1. 派生类的构造函数被调用时,会自动调用基类的构造函数初始化基类的那一部分成员,如果基类当中没有默认的构造函数,则必须在派生类构造函数的初始化列表当中显示调用基类的构造函数。
  2. 派生类的拷贝构造函数必须调用基类的拷贝构造函数完成基类成员的拷贝构造。
  3. 派生类的赋值运算符重载函数必须调用基类的赋值运算符重载函数完成基类成员的赋值。
  4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。
  5. 派生类对象初始化时,会先调用基类的构造函数再调用派生类的构造函数。
  6. 派生类对象在析构时,会先调用派生类的析构函数再调用基类的析构函数。

在编写派生类的默认成员函数时,需要注意以下几点:

  1. 派生类和基类的赋值运算符重载函数因为函数名相同构成隐藏,因此在派生类当中调用基类的赋值运算符重载函数时,需要使用作用域限定符进行指定调用。
  2. 由于多态的某些原因,任何类的析构函数名都会被统一处理为destructor();。因此,派生类和基类的析构函数也会因为函数名相同构成隐藏,若是我们需要在某处调用基类的析构函数,那么就要使用作用域限定符进行指定调用。
  3. 在派生类的拷贝构造函数和operator=当中调用基类的拷贝构造函数和operator=的传参方式是一个切片行为,都是将派生类对象直接赋值给基类的引用。

说明一下:
基类的构造函数、拷贝构造函数、赋值运算符重载函数我们都可以在派生类当中自行进行调用,而基类的析构函数是当派生类的析构函数被调用后由编译器自动调用的,我们若是自行调用基类的构造函数就会导致基类被析构多次的问题。
我们知道,创建派生类对象时是先创建的基类成员再创建的派生类成员,编译器为了保证析构时先析构派生类成员再析构基类成员的顺序析构,所以编译器会在派生类的析构函数被调用后自动调用基类的析构函数。

继承与友元

友元关系不能继承,也就是说基类的友元可以访问基类的私有和保护成员,但是不能访问派生类的私有和保护成员。

例如,以下代码中Display函数是基类Person的友元,当时Display函数不是派生类Student的友元,即Display函数无法访问派生类Student当中的私有和保护成员。

#include <iostream>
#include <string>
using namespace std;
class Student;
class Person
{
public:
	//声明Display是Person的友元
	friend void Display(const Person& p, const Student& s);
protected:
	string _name; //姓名
};
class Student : public Person
{
protected:
	int _id; //学号
};
void Display(const Person& p, const Student& s)
{
	cout << p._name << endl; //可以访问
	cout << s._id << endl; //无法访问
}
int main()
{
	Person p;
	Student s;
	Display(p, s);
	return 0;
}

若想让Display函数也能够访问派生类Student的私有和保护成员,只能在派生类Student当中进行友元声明。

class Student : public Person
{
public:
	//声明Display是Student的友元
	friend void Display(const Person& p, const Student& s);
protected:
	int _id; //学号
};

继承与静态成员

若基类当中定义了一个static静态成员变量,则在整个继承体系里面只有一个该静态成员。无论派生出多少个子类,都只有一个static成员实例。

例如,在基类Person当中定义了静态成员变量_count,尽管Person又继承了派生类Student和Graduate,但在整个继承体系里面只有一个该静态成员。
我们若是在基类Person的构造函数和拷贝构造函数当中设置_count进行自增,那么我们就可以随时通过_count来获取该时刻已经实例化的Person、Student以及Graduate对象的总个数。

#include <iostream>
#include <string>
using namespace std;
//基类
class Person
{
public:
	Person() 
	{ 
		_count++; 
	}
	Person(const Person& p) 
	{
		_count++;
	}
protected:
	string _name; //姓名
public:
	static int _count; //统计人的个数。
};
int Person::_count = 0; //静态成员变量在类外进行初始化
//派生类
class Student : public Person
{
protected:
	int _stuNum; //学号
};
//派生类
class Graduate : public Person
{
protected:
	string _seminarCourse; //研究科目
};
int main()
{
	Student s1;
	Student s2(s1);
	Student s3;
	Graduate s4;
	cout << Person::_count << endl; //4
	cout << Student::_count << endl; //4
	return 0;
}

此时我们也可以通过打印Person类和Student类当中静态成员_count的地址来证明它们就是同一个变量。

cout << &Person::_count << endl; //00F1F320
cout << &Student::_count << endl; //00F1F320

继承的方式

单继承:一个子类只有一个直接父类时称这个继承关系为单继承。

多继承:一个子类有两个或两个以上直接父类时称这个继承关系为多继承。

菱形继承:菱形继承是多继承的一种特殊情况。


从菱形继承的模型构造就可以看出,菱形继承的继承方式存在数据冗余和二义性的问题。

例如,对于以上菱形继承的模型,当我们实例化出一个Assistant对象后,访问成员时就会出现二义性问题。

#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
	string _name; //姓名
};
class Student : public Person
{
protected:
	int _num; //学号
};
class Teacher : public Person
{
protected:
	int _id; //职工编号
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; //主修课程
};
int main()
{
	Assistant a;
	a._name = "peter"; //二义性:无法明确知道要访问哪一个_name
	return 0;
}

Assistant对象是多继承的Student和Teacher,而Student和Teacher当中都继承了Person,因此Student和Teacher当中都有_name成员,若是直接访问Assistant对象的_name成员会出现访问不明确的报错。

对于此,我们可以显示指定访问Assistant哪个父类的_name成员。

//显示指定访问哪个父类的成员
a.Student::_name = "张同学";
a.Teacher::_name = "张老师";

虽然该方法可以解决二义性的问题,但仍然不能解决数据冗余的问题。因为在Assistant的对象在Person成员始终会存在两份。

菱形虚拟继承

为了解决菱形继承的二义性和数据冗余问题,出现了虚拟继承。如前面说到的菱形继承关系,在Student和Teacher继承Person是使用虚拟继承,即可解决问题。

虚拟继承代码如下:

#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
	string _name; //姓名
};
class Student : virtual public Person //虚拟继承
{
protected:
	int _num; //学号
};
class Teacher : virtual public Person //虚拟继承
{
protected:
	int _id; //职工编号
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; //主修课程
};
int main()
{
	Assistant a;
	a._name = "peter"; //无二义性
	return 0;
}

此时就可以直接访问Assistant对象的_name成员了,并且之后就算我们指定访问Assistant的Student父类和Teacher父类的_name成员,访问到的都是同一个结果,解决了二义性的问题。

cout << a.Student::_name << endl; //peter
cout << a.Teacher::_name << endl; //peter

而我们打印Assistant的Student父类和Teacher父类的_name成员的地址时,显示的也是同一个地址,解决了数据冗余的问题。

cout << &a.Student::_name << endl; //0136F74C
cout << &a.Teacher::_name << endl; //0136F74C

菱形虚拟继承原理

在此之前,我们先看看不使用菱形虚拟继承时,以下菱形继承当中D类对象的各个成员在内存当中的分布情况。


测试代码如下:

#include <iostream>
using namespace std;
class A
{
public:
	int _a;
};
class B : public A
{
public:
	int _b;
};
class C : public A
{
public:
	int _c;
};
class D : public B, public C
{
public以上是关于什么是继承???概念及作用(好处)?谢谢高手指点的主要内容,如果未能解决你的问题,请参考以下文章

C++进阶:继承C++为什么要引入继承 | 继承概念及定义 | 基类和派生类对象赋值转换 | 继承中的作用域 | 派生类的默认成员函数 | 继承与友元/静态成员 | 复杂的菱形继承及菱形虚拟继承

C++进阶:继承C++为什么要引入继承 | 继承概念及定义 | 基类和派生类对象赋值转换 | 继承中的作用域 | 派生类的默认成员函数 | 继承与友元/静态成员 | 复杂的菱形继承及菱形虚拟继承

C++进阶:继承C++为什么要引入继承 | 继承概念及定义 | 基类和派生类对象赋值转换 | 继承中的作用域 | 派生类的默认成员函数 | 继承与友元/静态成员 | 复杂的菱形继承及菱形虚拟继承

C++中的继承

1EJB基本概念及运行环境

require.js的基本概念及使用流程