本节知识点:
1.c++中的多重继承:
a.
c++在语法上支持多重继承,但是不推荐使用多重继承,因弊大于利!!!
b.
多重继承的弊端:
第一:多重继承带来的代码复杂性远多于其带来的便利,
多重继承对代码维护性上的影响是灾难性的,
即如果一个类是多重继承的,考虑这个类的时候,应该考虑他所有的父类,父类的所有父类,
原本单继承是一个树的结构,由于多重继承变成了图式结构,复杂性可想而知!!!所以在工程开发中真正意义上的多重继承几乎不被使用。
第二:多重继承中的多个父类中,如果有同名成员,就会导致子类对象在访问成员的时候,产生二义性,这种二义性是无法修复的!!!
代码如下:
#include <iostream>
using namespace std;
class parent1
{
public:
void fun()
{
cout << "parent1" << endl;
}
};
class parent2
{
public:
void fun()
{
cout << "parent2" << endl;
}
};
class child : public parent1 , public parent2
{
};
int main()
{
child c;
c.fun();
return 0;
}
第三:多重继承中的多个父类是兄弟关系类(即这些父类继承于同一个父类),他们在共同的父类中继承的成员,在子类中访问时候,会产生二义性,但是这种二义性不是致命的,是可以修复的!如图:
代码如下:
#include <iostream>
using namespace std;
class parentp
{
public:
void fun()
{
cout << "parentp" << endl;
}
};
class parent1 : public parentp
{
public:
};
class parent2 : public parentp
{
public:
};
class child : public parent1 , public parent2
{
};
int main()
{
child c;
c.fun();
return 0;
}
c.
c++中对于父类为兄弟关系类的多继承产生的二义性的解决方案:
这里引用一个新的概念,叫
虚继承(virtual public parent)。
为了解决从不同途径继承来的同名数据成员造成的二义性问题,可以将共同基类设置为虚基类。这时从不同的途径继承过来的同名数据成员在内存中就只有一个拷贝。这样子类中从多重继承的父类的共同父类中继承得来的同名成员,就只有一个了,就不会产生二义性了!!!
代码如下:
#include <iostream>
using namespace std;
class parentp
{
public:
int a;
void fun()
{
cout << "parentp" << endl;
}
};
class parent1 : virtual public parentp
{
public:
};
class parent2 : virtual public parentp
{
public:
};
class child : public parent1 , public parent2
{
};
int main()
{
child c;
c.fun();
return 0;
}
d.对于虚继承仍然存在的问题(其实虚继承根本就没有从理论上解决问题):
何时使用虚继承,根据就确定不下来,因为那是将来的事情,在写父类的时候,完全不知道,事后的那个子类是需要多重继承的。所以在大的工程代码中,很难控制这个虚继承,而且虚继承使用多了还很占用效率。
2.c++中的接口概念(接口是多重继承的一个经典的应用):
a.
c++中不像java和c#一样,有接口的概念。因为java和c#中不支持多重继承,所以提出了接口的概念。而c++支持多重继承,本身没有接口的概念。但是可以通过使用纯虚函数实现接口!
b.
c++中的接口,其实就是一个很特殊的类,这个类里面全部都是纯虚函数(全部函数都只是有函数原型,没有函数体),这个类就是用来继承的,且继承的子类中必须有接口中纯虚函数的函数重写,即实现接口函数的具体功能。它与抽象类的区别就是,抽象类里面可以定义有函数体的函数,而接口不可以。
c.
实际工程经验证明,多重继承接口不会带来二义性和复杂性等问题!!!多重继承可以通过精心设计用单继承和接口来代替(因为这样不会产生二义性和复杂性)!!!
代码如下:
#include <iostream>
using namespace std;
class Interface
{
public:
virtual int add(int a, int b) = 0;
virtual int mix(int a, int b) = 0;
};
class Interface1
{
public:
virtual int add(int a, int b) = 0;
virtual int max(int a, int b) = 0;
};
class fun : public Interface, public Interface1
{
public:
int add(int a, int b)
{
return a+b;
}
int max(int a, int b)
{
return (a>b)?a:b;
}
int mix(int a, int b)
{
return (a<b)?a:b;
}
};
int main()
{
fun f;
cout << f.add(5,4) << endl;
cout << f.max(5,4) << endl;
cout << f.mix(5,4) << endl;
return 0;
}
注意:可见上面的代码中
virtual int add(int a, int b) = 0;
这条语句没有产生二义性。为什么没有产生二义性:没有产生二义性其实跟是不是纯虚函数没有关系,其实跟是不是虚函数也没有关系。
这里选择纯虚函数仅仅是为了符合面向对象编程中接口的定义(因为纯虚函数是没有函数体的,所以在继承的子类中,就必须重写这个纯虚函数的函数体,就满足了接口的定义,同时也强制了子类中必须重写,避免了二义性)。
其实没有产生二义性,是因为发生了函数的重写,子类中重新定义了多重继承的父类中的同名成员,发生了函数的重写,此时子类对象访问同名成员的时候,根本就没有二义性,因为子类中重写的函数(即子类中同名成员)隐藏了父类同名成员。只有一种可能,就是调用子类成员,有什么二义性!!!
代码如下:
#include <iostream>
using namespace std;
class Interface
{
public:
int add(int a, int b)
{
cout << "hello" <<endl;
}
virtual int mix(int a, int b) = 0;
};
class Interface1
{
public:
int add(int a, int b)
{
cout << "world" <<endl;
}
virtual int max(int a, int b) = 0;
};
class fun : public Interface, public Interface1
{
public:
int add(int a, int b)
{
return a+b;
}
int max(int a, int b)
{
return (a>b)?a:b;
}
int mix(int a, int b)
{
return (a<b)?a:b;
}
};
int main()
{
fun f;
cout << f.add(5,4) << endl;
cout << f.max(5,4) << endl;
cout << f.mix(5,4) << endl;
return 0;
}
d.
通过上面注意所说,可以知道。虚继承避免的二义性和接口避免的二义性的原理是不一样的:接口是通过子类中进行同名成员的重写定义(如函数重写),使得子类中隐藏父类同名成员的方式,避免了二义性(即子类中通过重写隐藏了父类的同名成员,所以没有二义性)!虚继承,是通过使兄弟关系的父类从祖父类中标记为虚继承,从而让子类在兄弟关系的父类的多重继承时,在子类中只继承一套祖父类的成员的方法,来避免二义性的(即子类中不存在同名成员,因为只继承了一套,所以没有二义性)!
e.
接口类只是一个功能说明,而不是功能实现。子类需要根据功能说明定义功能实现。
本届补充:
1.多重继承的格式:
多重继承时,格式应该是这样的
class fun : public Interface, public Interface1 每一个类前面都应该加上继承属性(即使public继承,还是private继承),类与类之间用逗号相隔。