C++多重继承

多重继承(Multiple Inheritance)描述的是有多个直接基类的类。比如公有MI表示的也是is-a的关系。
例如,可以重类B和类C派生出类D:

class D :public B, public C
{
	...
};

多重继承存在两个问题:

  • 从两个不同的基类继承同名方法(B、C中有同名的方法怎么处理);
  • 从两个或者更多相关基类那里继承同一个类的多个实例(类B 和类C都是从类A派生出来的,这样D将有两个A的实例);

示例:

#include<iostream>
using namespace std;

class A
{
private :
	int a;
public:
	A() :a(0) {}
	A(int n) :a(n) {}
	virtual ~A() {};
	virtual void set() { std::cin >> a; }
	virtual void show()const { cout << " A:" << a << endl; }
	virtual void other() = 0;//纯虚函数-----A是抽象基类
};
class B: virtual public A
{
private:
	int b;
public:
	B() :A(0), b(0) {};
	B(int m, int n = 0) :A(m), b(n) {}
	virtual ~B() {}
	virtual void set() { std::cin >> b; }
	virtual void show() const{ cout << " B:" << b << endl; }
	virtual void other() { cout << "B other" << endl; }
};
class C : virtual public A
{
private:
	int c;
public:
	C() :A(0), c(0) {};
	C(int m, int n = 0) :A(m), c(n) {};
	virtual ~C() {};
	virtual void set() { std::cin >> c; }
	virtual void show()const { cout << " C:" << c << endl; }
	virtual void other() { cout << "C other" << endl; }
};

class D :public B, public C
{
private:
	int d;	
public:
	D():A(0),B(0,0),C(0,0),d(0) {}
	D(int m, int n, int x, int y):A(m), B(m, n), C(m, x), d(y) {}
	virtual ~D() {}
	virtual void set() 
	{ 
		A::set();
		B::set();
		C::set();
		std::cin >> d; 
	}
	virtual void show()const 
	{ 
		A::show();
		B::show();
		C::show();
		cout << " D: "<<d << endl; 
	}
	virtual void other() { cout << "D other" << endl; }
};
void main()
{
	cout << "Multiple Inheritance" << endl;
	A *p1 = new D(1, 2, 3, 4);
	p1->show();
	p1->set();
	p1->show();
	delete p1;
	D dd(1, 2, 3, 4);
	dd.B::show();
}

关于虚基类:
虚基类使得从多个类(他们的基类相同)派生出的对象只继承一个基类。虚继承的提出是为了解决多重继承时,可能会保存两份副本的问题,也就是说用了虚继承就只保留了一份副本,但是这个副本是被多重继承的基类所共享的。

class B: virtual public A{...}
class C : virtual public A{...}
class D :public B, public C{...}

通过虚基类,D中将不会存在两个A对象,即D继承的B和C对象共享一个A对象。这时候就可以使用多态了,基类A的指针可以指向派生类D对象。
注:虚函数和虚基类之间不存在明显的联系,这里使用了virtual-有点像关键字重载。

新的构造函数规则:
C++在基类是虚的时候,禁止信息通过中间类自动传递给给基类。必须显式调用基类的构造函数:

	D(int m, int n, int x, int y):A(m), B(m, n), C(m, x), d(y) {}

关于使用哪个方法:
通过作用域解析运算符进行区分。

关于对象内存大小:

cout << sizeof(A) << endl;//8
cout << sizeof(B) << endl;//20
cout << sizeof(C) << endl;//20
cout << sizeof(D) << endl;//32

sizeof(A)=整型变量a(4字节)+一个虚表指针(4字节)
=8字节。

sizeof(B)=整型变量b(4字节)+一个虚表指针(4字节)
+类A中的整型变量a(4字节)+类A虚表指针(4字节)
+指向A的虚基类指针(4字节)
=20字节

sizeof(C )=整型变量c(4字节)+一个虚表指针(4字节)
+类A中的整型变量a(4字节)+类A虚表指针(4字节)+
指向A的虚基类指针(4字节)
=20字节

sizeof(D)=整型变量d(4字节)+一个虚表指针(4字节)
+类B中的整型变量b(4字节)+类B虚表指针(4字节)
+类C中的整型变量C(4字节)+类C虚表指针(4字节)
+共享类A中的整型变量a(4字节)+指向A的虚基类指针(4字节)
=32字节

参考:https://blog.youkuaiyun.com/sunshinewave/article/details/51079204
https://blog.youkuaiyun.com/m0_38014304/article/details/84286266
https://blog.youkuaiyun.com/Neo_dot/article/details/80725800

### C++多重继承的使用方法与问题解决方案 #### 一、多重继承的基本概念 C++允许多重继承,即一个派生类可以有多个基类。这种特性允许程序员通过组合不同的功能模块来构建复杂的类结构[^2]。 ```cpp class Base1 { public: void funcBase1() { std::cout << "Function from Base1\n"; } }; class Base2 { public: void funcBase2() { std::cout << "Function from Base2\n"; } }; class Derived : public Base1, public Base2 {}; // 多重继承 ``` 上述代码展示了如何定义一个多继承的类`Derived`,它同时继承自`Base1`和`Base2`。 --- #### 二、多重继承可能引发的问题 ##### 1. **同一个对象的不同地址** 当存在多条路径到达同一基类时,可能会导致该基类被多次实例化,从而使得同一个对象具有不同的地址。 ##### 2. **数据冗余** 如果两个基类共享某些成员变量或函数,则可能导致重复的数据存储,增加内存开销并降低效率。 ##### 3. **虚函数表冲突** 在多重继承的情况下,编译器会为每个基类创建独立的虚函数表(vtable)。这可能导致复杂性和性能下降。 --- #### 三、菱形继承问题及其解决办法 ##### 1. **什么是菱形继承?** 菱形继承是指在一个多重继承体系中,派生类通过两条或多条路径间接继承了相同的基类。这种情况容易造成歧义和资源浪费[^1]。 示例: ```cpp class A {}; class B : public A {}; class C : public A {}; class D : public B, public C {}; // 菱形继承 ``` 在此例子中,`D`通过`B`和`C`两次继承了`A`,因此会出现两份`A`的副本。 ##### 2. **虚拟继承的作用** 为了消除菱形继承带来的问题,C++引入了虚拟继承的概念。通过声明基类为虚基类,可以确保无论有多少条路径通向该基类,在最终的对象模型中只保留一份唯一的拷贝。 修改后的代码如下: ```cpp class A {}; class B : virtual public A {}; class C : virtual public A {}; class D : public B, public C {}; // 正确处理菱形继承 ``` 此时,即使`D`仍然从`B`和`C`继承而来,但由于两者都采用了虚拟继承方式,所以整个系统只会维护单一版本的`A`。 --- #### 四、同名函数引起的二义性问题及解决方案 ##### 1. **问题描述** 当两个基类拥有相同名称的方法时,直接调用这些方法会导致编译错误,因为编译器无法判断具体应该选用哪一个实现[^3]。 示例: ```cpp class Parent1 { public: void print() { std::cout << "Parent1's Print Function.\n"; } }; class Parent2 { public: void print() { std::cout << "Parent2's Print Function.\n"; } }; class Child : public Parent1, public Parent2 {}; int main() { Child obj; obj.print(); // 编译错误:二义性 } ``` ##### 2. **解决策略** 可以通过作用域解析运算符显式指定要调用的具体父类方法: 修正版代码: ```cpp obj.Parent1::print(); // 明确指向Parent1的实现 obj.Parent2::print(); // 明确指向Parent2的实现 ``` 或者也可以在子类内部重新定义此方法以覆盖原有行为,并提供统一接口给外部使用者访问。 --- ### 总结 多重继承虽然强大但也伴随着诸多挑战,合理运用诸如虚拟继承等机制能够有效规避潜在风险;而对于因命名冲突所造成的困扰则需借助于明确的作用域指示加以化解[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值