C+多继承中存在的问题

1.多继承
1.1优点:
代码复用性:多继承可以从多个父类中继承不同的属性和方法,从而提高了代码复用性。
灵活性:多继承可以让一个类拥有不同父类的多种特性和方法,从而使得类更加灵活。
逻辑清晰:多继承可以更好地反映实际情况中的继承关系,使代码的逻辑更加清晰。
1.2缺点:
命名冲突:多个父类中可能存在相同名称的属性和方法,这就可能导致命名冲突的问题。
复杂性:多继承会增加代码的复杂性,使得代码难以维护和调试。
继承层数过多:多继承会导致类的继承层数过多,这就可能使得代码的性能受到影响。
因此,在使用多继承时需要仔细考虑其优缺点,权衡其利弊,以便在不同的情况下选择最适合的编程方式。
 

2.多继承中引发的菱形继承问题

2.1什么是菱形继承

菱形继承是指一个类同时继承了两个不相关的父类,并且这两个父类又继承了同一个父类,从而形成一个菱形的继承关系。这种继承关系会导致以下几个弊端:

内存浪费:由于菱形继承会出现多个重复的父类成员变量,导致子类对象的内存空间被浪费。

命名冲突:由于不同的父类可能会有同名的方法或成员变量,因此在子类中访问这些成员变量或方法时,会导致命名冲突的问题。

继承关系不清晰:由于菱形继承会使得继承关系变得非常复杂,从而降低了代码的可读性和可维护性。

难以理解和调试:由于菱形继承的复杂性,使得代码更难以理解和调试。特别是在出现问题时,需要花费更多的时间和精力来查找和解决问题。

综上所述,菱形继承虽然能够实现代码的重用,但是由于其带来的弊端,使用时需要谨慎考虑。如果可能的话,应该避免使用菱形继承,或者通过其他方式来实现代码的重用。

 菱形继承结构图

 3.菱形结成引发的问题

问题:数据冗余和二义性 

代码示例:

#define _CRT_SECURE_NO_WARNINGS
//继承问题
using namespace std;
#include <stdio.h>
#include<iostream>
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:
	int _d;
};
int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	cout << d.B::_a << endl;
	cout << sizeof(D) << endl;
	return 0;
}

 如上代码,先有基类A,而B和C继承A,D又继承了B和C,这就出现了菱形继承问题,在创建D实例对象d时,档要对_a进行定义时,不知道对c钟的_a还是b钟的_a定义,这就是出现了二义性,而且在菱形继承结构中,基类 A 的成员在类 D 中会有两份拷贝,分别由类 B 和类 C 继承而来。这就造成了内存浪费。如上使用d.B::_a = 1; d.C::_a = 2; 这种方式虽然可以避免代码出现二义性 但是解决不了代码冗余问题,正确做法是:使用虚继承

#include <iostream>
class A {
public:
	int _a;
};
class B : virtual public A { // 使用 virtual 关键字
public:
	int _b;
};
class C : virtual public A { // 使用 virtual 关键字
public:
	int _c;
};
class D : public B, public C {
public:
	int _d;
};
int main()
{
	D d;
	d._a = 1; // 此处直接访问类 A 中的成员变量 _a
	d._b = 2;
	d._c = 3;
	d._d = 4;
	std::cout << d._a << std::endl;
	std::cout << sizeof(D) << std::endl;
	return 0;
}

解决:菱形虚拟继承

菱形虚拟继承是一种使用虚继承解决多重继承中数据冗余和二义性问题的技术。

在菱形虚拟继承中,一个类同时从两个或多个基类继承相同的虚基类,这些基类的虚基类又相同,从而形成了一个菱形的继承关系。如果不使用虚拟继承,将会出现同样的虚基类被不同的子类分别继承的情况,从而造成数据冗余和二义性问题。而使用虚拟继承,可以让这些虚基类在内存中只存在一份,避免数据冗余和二义性问题的发生。

解决数据冗余的问题的方法是,对于同一个虚基类,使用虚拟继承可以确保其在内存中只存在一份,从而避免了多个子类继承同一份数据的情况。这样,当某个子类需要访问虚基类的成员时,只需要通过一个虚基类指针即可访问,避免了数据的冗余。

解决二义性的问题的方法是,在虚拟继承的情况下,会在子类对象的内存布局中添加一个虚基类指针,指向虚基类对象在内存中的位置。这个指针可以帮助编译器判断需要访问的是哪个虚基类对象的成员,从而避免了二义性的问题。

总之,菱形虚拟继承是一种使用虚拟继承来解决多重继承中数据冗余和二义性问题的技术。使用虚拟继承可以确保同一个虚基类在内存中只存在一份,避免了数据冗余,同时在子类对象中添加虚基类指针,避免了二义性的问题。

4. 继承和组合的区别?什么时候用继承?什么时候用组合?

继承是面向对象编程中的一种机制,它允许新的类在已有类的基础上进行扩展和修改。子类可以继承父类的成员和方法,并且可以重写父类的方法或添加新的方法和成员。继承具有代码复用、方便扩展和多态等特点。继承的缺点是子类和父类之间的耦合度比较高,子类对父类的依赖性较强,父类的修改可能会影响到子类的功能,同时子类也可能会继承不需要的功能,导致代码冗余。

组合是将多个对象组合在一起形成新的对象,以实现新的功能或行为的方式。组合是将多个类或对象通过引用或指针关联在一起,使得一个类可以包含另一个类的对象,并且可以调用该对象的方法和成员。组合具有灵活性和高度的解耦合性,因为对象之间的关系可以通过指针或引用动态建立和解除,可以随时修改组合关系,不会影响到类本身的实现和行为。组合的缺点是需要花费更多的时间和精力来设计和管理对象之间的关系,并且不同的对象之间的通信和调用可能会比较繁琐。

通常情况下,应该优先考虑组合而不是继承。只有在确实需要从现有类中继承行为或属性时,才应该使用继承。一般来说,当新类和已有类之间具有"is-a"关系时(比如,一只狗是一种动物),可以考虑使用继承。而当新类和已有类之间具有“has-a”关系时(比如,一个房子有一扇门),可以考虑使用组合。此外,如果一个类需要从多个类中继承行为或属性,但是又不想产生多重继承的问题时,可以使用接口或抽象类进行设计,这样既能实现代码复用,又能避免多重继承带来的问题

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
重继承的菱形继承问题是指在一个继承体系,某个子类同时继承了两个不同的父类,而这两个父类又共同继承自同一个父类。这样就形成了一个菱形的继承结构,如下所示: A / \ B C \ / D 在这个结构,子类 D 继承了父类 B 和父类 C,而父类 B 和父类 C 又都继承自父类 A。 菱形继承问题可能导致以下几个问题: 1. 冗余数据:由于父类 B 和父类 C 都继承自父类 A,子类 D 在继承时会包含来自父类 A 的成员变量和方法。这样就会导致在子类 D 存在两份相同的成员变量和方法,造成冗余。 2. 命名冲突:如果父类 B 和父类 C 定义了相同名称的成员变量或方法,子类 D 在继承时将无法区分使用哪个父类的成员。 3. 多重继承的二义性:如果子类 D 调用了一个在父类 B 和父类 C 都有定义的方法,那么就会出现二义性,编译器无法确定应该使用哪个父类的方法。 为了解决这些问题,可以采取以下方法: 1. 虚继承(virtual inheritance):在父类 B 和父类 C 继承父类 A 时,使用虚继承,可以避免冗余数据的问题。虚继承可以确保在菱形继承共享同一个父类的成员,避免重复拷贝。 2. 重写(override):如果子类 D 继承了父类 B 和父类 C 重名的方法或成员变量,可以在子类 D 重写这些方法或成员变量,以解决命名冲突的问题。 3. 作用域解析运算符(scope resolution operator):如果子类 D 需要调用父类 B 和父类 C 重名的方法,可以使用作用域解析运算符来指定使用哪个父类的方法。 需要注意的是,在设计继承体系时,尽量避免菱形继承结构的出现,以减少代码的复杂性和潜在的问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值