c++的继承 《进阶二》《继承与静态成员》《复杂的菱形继承及菱形虚拟继承》《虚拟继承解决数据冗余和二义性的原理》 加《笔试面试题》

由于上面的内容比较多,所以我觉的应该写两篇内容,要不太多了,容易看烦了,所以我们再写一篇博客,在一起学习一下。
六. 继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。
我们看一下下面的代码;

#include<iostream>
#include<string.h>

using namespace std;

class Person
{
public:
	Person() 
	{
		++_count; 
	}
protected:
	string _name; // 姓名
public:
	static int _count; // 统计人的个数。
};
     int Person::_count = 2;
class Student : public Person
{
protected:
	int _stuNum; // 学号
};
class Graduate : public Student
{
protected:
	string _seminarCourse; // 研究科目
};
void TestPerson()
{
	//Person  p0;
	Student s1;
	Student s2;
	Student s3;
	Graduate s4;
	cout << " renshu:" << Person::_count << endl;
	Student::_count = 0;
	cout << " renshu:" << Person::_count << endl;
}
int main()
{
	TestPerson();
	return 0;
}

根据代码,我们就可以看出继承和static成员之间,在基类定义了一个静态成员,那么在这个在整个体系中,只有一个这样的成员。(试一试,反正也不会坏,所以,把其他的也改为static,试一试,你就会发现其中的情况)

七.复杂的菱形继承及菱形虚拟继承
单继承:一个子类只有一个直接父类时称这个继承关系为单继承
在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述
菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。

#include<string>
#include<iostream>
using namespace std;

class Preson{
public:
	string _name;
};
class Student :public Preson
{
protected:
	int _num;
};
class Tearch : public Preson
{
protected:
	int _id;
};
class Assistant :public Student, public Tearch
{
protected:
	string _majorCourse;
};
void Test()
{
	Assistant a;
	//a._name = "dpfnmt";  //如果我直接使用这个a调用,那么就会报错,显示:不知道调用谁的,指明不明确;
	
//所以要使用下面的调用;
	a.Student::_name = "dpf";
	a.Tearch::_name = "nmt";
	cout << a.Student::_name << endl;
	cout << a.Tearch::_name << endl;
}
int main()
{
	Test();
	return 0;
}

就像上面打印的代码一样,使用类名调用可以解决不知道调谁的错误,但是却不能解决代码冗余的问题《白话就是说:代码量使用大量,而且重复》

那么该怎么样结局这样的问题,这里我们就要说一个重要的关键字 《 virtual 》。
直接看下面一段代码:

class Preson{
public:
	string _name;
};
class Student :virtual public Preson
{
public:
	int _num;
};
class Tearch :virtual public Preson
{
public:
	int _id;
};
class Assistant :public Student, public Tearch
{
public:
	string _majorCourse;
};

int main()
{
	Assistant a;
	a.Student::_name = "dpf";
	a.Tearch::_name = "nmt";
	a._num = 5;
	a._id = 123;
	a._majorCourse = "yuwen";

cout << a.Student::_name << endl;
	cout << a.Tearch::_name << endl;
	cout << a._num <<" "<< a._id << " " <<a._majorCourse << endl;

return 0;
}

在这里插入图片描述
看着打印的结果,我们就可以知道了,这个关键字的厉害吧。

下面还要看一看别的:
虚拟继承解决数据冗余和二义性的原理

为了研究虚拟继承原理,我们给出了一个简化的菱形继承继承体系,再借助内存窗口观察对象成员的模型。

我们把上面的代码,改一改,就变成了下面的代码,下面我们就由内存串口看一下。

class Preson{
public:
int _name;
};
class Student :virtual public Preson
{
public:
int _num;
};
class Tearch :virtual public Preson
{
public:
int _id;
};
class Assistant :public Student, public Tearch
{
public:
int _majorCourse;
};

int main()
{
Assistant a;
a.Student::_name = 1;
a.Tearch::_name = 2;
a._num = 3;
a._id = 4;
a._majorCourse = 5;

cout << a.Student::_name << endl;
cout << a.Tearch::_name << endl;
cout << a._num <<" "<< a._id << " " <<a._majorCourse << endl;
return 0;
}

这里有一个问题,不知道为什么就是解决不了,第一个,他应该是调用基类的函数,但是后面还是对着;在这里插入图片描述
由上面的执行程序可以看出,前面发执行是正确的,但是不知道为什么第一个调用基类的他没有改变;
虚拟继承解决数据冗余和二义性的原理

为了研究虚拟继承原理,我们给出了一个简化的菱形继承继承体系,再借助内存窗口观察对象成员的模型。不知道我的电脑的问题,还是怎么回事,我的就是调用不出来,我下来再试一试,如果有感兴趣的同学可以试一试。调用成功的话,就是可以。

下图是菱形虚拟继承的内存对象成员模型:这里可以分析出D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。
下面这句话是非常重要,记住虚基表表示的是:存的偏移量。

八,继承的总结和反思
1.很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题。

  1. **多继承可以认为是C++的缺陷之一,**很多后来的OO语言都没有多继承,如Java。

  2. 继承和组合
    (1) public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
    (2)组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
    (3)优先使用对象组合,而不是类继承 。

(4)**继承允许你根据基类类的实现来定义派生类的实现。**这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见。继承一定程度破坏了基类的封装,基类的改变,对派生类类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。

(5)对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。 组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。

(6)**实际尽量多去用组合。组合的耦合度低,代码维护性好。**不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用继承,可以用组合,就用组合。

总结为:两者有自己的优缺点,相互制约,这里没有确切说哪一种比较好,有自己的使用场景,根据自己的需要,使用自己的需要的,《组合或者继承》

下面还有问题,根据自己的能力,回答问题,如果不会的同学,我可以在下面写这答案。
九,笔试面试题

  1. 什么是菱形继承?菱形继承的问题是什么?
  2. 什么是菱形虚拟继承?如何解决数据冗余和二义性的
  3. 继承和组合的区别?什么时候用继承?什么时候用组合?
  4. 多继承中指针偏移问题?

解答(1)菱形继承是多继承的一种特殊情况。
问题:菱形继承有数据冗余和二义性 。

解答(2) 这给问题可以根据上面所介绍的理解,想使用虚拟继承就要加上关键字“virtual” ,就像前面的一样,可以解决二义性问题,但是不能解决数据冗余。这里要由内存哪里看,我这里看不出来,下来,我再把博客这里改一改;

解答(3) 1.组合定义
在新类里面创建原有类的对象,重复利用已有类的功能。(has-a关系)
2.继承定义
可以使用现有类的功能,并且在无需重复编写原有类的情况下对原有类进行功能上的扩展。(is-a关系)
《这个问题可以看上面第八节,讲的很清楚。》

解答(4)这个问题我可能回答不好,在网上我看了许多,觉得下面这个博主讲的很好,所以可以借鉴一下,我把链接发在下面。:https://blog.csdn.net/theonegis/article/details/45796229(请认真的看,可以借鉴他写的代码,实现一下,真的很好)

猜你喜欢

转载自blog.csdn.net/dpfxaca6/article/details/88784387