【继承】复杂的菱形继承

图片名称

博主首页: 有趣的中国人
 

专栏首页: C++进阶
 


本篇文章主要讲解 菱形继承 的相关内容

目录

1. 继承与友元

2. 继承与静态成员

3. 复杂的菱形继承及菱形虚拟继承

3.1 继承分类

3.2 菱形继承导致的问题

3.3 虚拟继承解决数据冗余的原理

4. 继承和组合的区别


 1. 继承与友元

友元关系不能继承,也就是说,基类的友元函数不能访问子类的保护和私有成员。

class Student;
class Person
{
public:
	friend void Display(const Person& p, const Student& s);
protected:
	string _name = "Jason"; // 姓名
};
class Student : public Person
{
protected:
	int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
	cout << p._name << endl;
    // 此处不能访问
	//cout << s._stuNum << endl;
}
void main()
{
	Person p;
	Student s;
	Display(p, s);
}

2. 继承与静态成员

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

我们可以利用这个特性计算构造出了多少个继承的对象,只要在构造函数中每次将静态成员变量自增1即可。

class Person
{
public:
	Person() { ++_count; }
protected:
	string _name; // 姓名
public:
	static int _count; // 统计人的个数。
};

int Person::_count = 0;

class Student : public Person
{
protected:
	int _stuNum; // 学号
};

class Graduate : public Student
{
protected:
	string _seminarCourse; // 研究科目
};

void TestPerson()
{
	Student s1;
	Student s2;
	Student s3;
	Graduate s4;
	cout << " 人数 :" << Person::_count << endl;
	Student::_count = 0;
	cout << " 人数 :" << Person::_count << endl;
}

int main()
{
	TestPerson();
	return 0;
}

3. 复杂的菱形继承及菱形虚拟继承

3.1 继承分类

单继承

一个子类只有一个直接父类叫做做单继承。

多继承

一个子类有两个即以上的直接父类叫做多继承。

菱形继承

菱形继承是特殊的多继承。

3.2 菱形继承导致的问题

菱形继承可能导致两个问题:

1. 在上面的菱形继承中,假设Person中有一个成员变量_name,那么在Student和Teacher中也会有一个成员变量_name,然而Assistant继承了Student和Teacher,在Assistant中就会有两份_name,会导致数据冗余。 

2. 在访问Assistant中的_name时,由于不知道要访问的到底是哪个_name,是Student还是Teacher呢?就会有二义性的问题。 

class Person
{
public:
	string _name;
};

class Student : public Person
{
protected:
	int _stuid;
};

class Teacher : public Person
{
protected:
	int _teacherid;
};

class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse;
};

void test()
{
	Assistant ast;
	// 这里会有二义性的问题,不知道访问的是哪一个类中的
	ast._name = "a";

	// 指定作用域可以解决二义性的问题,但是无法解决数据冗余的问题
	ast.Teacher::_name = "xxx";
	ast.Student::_name = "yyy";
}

可以用虚拟继承来解决数据冗余的问题,只需要在Student和Teacher的继承Person时使用虚拟继承,加virtual关键字。

class Student : virtual public Person
{
protected:
	int _stuid;
};

class Teacher : virtual public Person
{
protected:
	int _teacherid;
};

 3.3 虚拟继承解决数据冗余的原理

我们可以用内存窗口调试下面这段代码:

class A
{
public:
	int _a;
};
class B : public A
//class B : virtual public A
{
public:
	int _b;
};
class C : public A
//class C : virtual public A
{
public:
	int _c;
};
class D : public B, public C
{
public:
	int _d;
};

int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}

如果不加virtual:

很明显_a有两份

 如果加了virtual:

 那么在B类和C类中,除了存储了继承下来的成员变量,头部存储的是个地址,我们可以看一下:

这个地址存储的含义是这个位置距离共同继承的A的偏移量,虚拟继承之后共同继承的部分就只有一份了,就解决的数据冗余的问题,我们可以用计算器验证以下:

 所以我们可以看出虚拟继承在内存中的存储方式:


4. 继承和组合的区别

1. public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
2. 合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
3. 优先使用对象组合,而不是类继承
4. 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称
为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的
内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很
大的影响。派生类和基类间的依赖关系很强,耦合度高。
5. 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象
来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复
用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。
组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被
封装。
6. 实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有
些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用
继承,可以用组合,就用组合。

  • 39
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值