C++语言是支持多继承的,这在java,C#是支持不允许多继承的
一个子类可以拥有多个子类
子类拥有所有父类的成员变量
子类继承所有父类的成员函数
子类对象可以当作任意父类对象使用
语法规则:
class Derived: public Base A,
public Base B,
public Base C,
{
};
多重继承带来的第一个问题
例子:
#include <iostream>
#include <string>
using namespace std;
class A
{
private:
int mA;
public:
A(int a)
{
mA = a;
}
int getA()
{
return mA;
}
};
class B
{
private:
int mB;
public:
B(int b)
{
mB = b;
}
int getB()
{
return mB;
}
};
class C : public B,public A
{
private:
int mC;
public:
C(int a,int b,int c) : B(b),A(a)
{
mC = c;
}
int getC()
{
return mC;
}
void print()
{
cout<<"ma = "<<getA()<<","
<<"mb = "<<getB()<<","
<<"mc = "<<mC<<endl;
}
};
int main()
{
cout<<"sizeof(c) ="<<sizeof(C)<<endl;
C c(1,2,3);
c.print();
return 0;
}
结果:
sice@sice:~$ ./a.out
sizeof(c) =12
ma = 1,mb = 2,mc = 3
之前我们说在一个类里面成员变量和成员函数是分开存放的,每一个类对象都有自己的成员变量但是共享成员函数,sizeof©=12就是三个int类型变量的字节数
改进上述代码的main函数
int main()
{
cout<<"sizeof(c) ="<<sizeof(C)<<endl;
C c(1,2,3);
c.print();
A *pa = &c;
B *pb = &c;
cout<<endl;
cout<<"pa->getA()"<<pa->getA()<<endl;
cout<<"pb->getB()"<<pb->getB()<<endl;
cout<<endl;
void *paa = pa;
void *pbb = pb;
if( paa == pbb )
{
cout << "Pointer to the same object!" << endl;
}
else
{
cout << "Error" << endl;
}
cout << "p = " << pa << endl;
cout << "pb = " << pb << endl;
cout << "paa = " << paa << endl;
cout << "pbb = " << pbb << endl;
return 0;
}
结果:
sizeof(c) =12
ma = 1,mb = 2,mc = 3
p->getA()1
pb->getB()2
Error
p = 0xbff06958
pb = 0xbff06954
paa = 0xbff06958
pbb = 0xbff06954
可以看出父类指针指向子类时只能访问父类中的成员函数,但是这里的为什么打印的地址不同呢?这是因为通过多继承得到的对象可能拥有"不同地址"!!,这是C++的特性,无解决方案,只能人工判断
多重继承带来的第二个问题![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/40849d997b5f5a7ae47a730ff0fae036.png)
多重继承可能会产生多余的成员,比如上图的Doctor类继承了Teacher类和Student类,就变成了两个name和两个age,当多重继承关系出现闭合时将产生数据多余的问题
解决方案:虚继承
class People{};
class Teacher : virtual public People{};
class Student : virtual public People{};
class Doctor:public Teadcher,public Student
{};
虚继承能够解决数据多余问题
中间层父类不再关心顶层父类的初始化
最终子类必须直接调用顶层父类的构造函数,必须找到父类,麻烦
虚继承使得架构设计可能出现问题!!
例子(不参用虚继承):
string>
using namespace std;
class People
{
string m_name;
int m_age;
public:
People(string name,int age)
{
m_name = name;
m_age = age;
cout<<"People(string name,int age)"<<endl;
}
void print()
{
cout<<"the name is "<<m_name<<endl;
cout<<"the age is "<<m_age<<endl;
}
};
class Teacher : virtual public People
{
public:
Teacher(string name,int age): People(name,age)
{
cout<<"Teacher(string name,int age)"<<endl;
}
};
class Student :virtual public People
{
public:
Student(string name,int age): People(name,age)
{
cout<<"Student(string name,int age)"<<endl;
}
};
class Doctor : public Teacher,public Student
{
public:
Doctor(string name,int age):Teacher(name, age),Student(name, age),People(name, age)
{
cout<<"Doctor(string name,int age)"<<endl;
}
};
int main()
{
Doctor d("Li",33);
d.print();
return 0;
}
结果:
People(string name,int age)
Teacher(string name,int age)
People(string name,int age)
Student(string name,int age)
Doctor(string name,int age)
the name is Li
the age is 33
the name is Li
the age is 33
例子(用虚继承):
#include <iostream>
#include <string>
using namespace std;
class People
{
string m_name;
int m_age;
public:
People(string name,int age)
{
m_name = name;
m_age = age;
cout<<"People(string name,int age)"<<endl;
}
void print()
{
cout<<"the name is "<<m_name<<endl;
cout<<"the age is "<<m_age<<endl;
}
};
class Teacher :virtual public People
{
public:
Teacher(string name,int age): People(name,age)
{
cout<<"Teacher(string name,int age)"<<endl;
}
};
class Student :virtual public People
{
public:
Student(string name,int age): People(name,age)
{
cout<<"Student(string name,int age)"<<endl;
}
};
class Doctor : public Teacher,public Student
{
public:
Doctor(string name,int age):Teacher(name,age),Student(name,age),People(name,age)
{
cout<<"Doctor(string name,int age)"<<endl;
}
};
int main()
{
Doctor d("Li",33);
d.print();
return 0;
}
结果:
People(string name,int age)
Teacher(string name,int age)
Student(string name,int age)
Doctor(string name,int age)
the name is Li
the age is 33
可以看出使用虚继承,中间层父类不再关心顶层父类的初始化,最终子类直接调用父类的构造函数,但是这在大工程是没必要存在的
多重继承可能会产生多个虚函数表
例子:
#include <iostream>
#include <string>
using namespace std;
class A
{
public:
virtual void funA()
{
cout<<"funA()"<<endl;
}
};
class B
{
public:
virtual void funB()
{
cout<<"funB()"<<endl;
}
};
class C: public A,public B
{
};
int main()
{
C c;
cout<<"sizeof(c)"<<sizeof(c)<<endl;
A *a = &c;
B *b = &c;
B *bb = (B*)a;
a->funA();
b->funB();
bb->funB();
}
```‘
结果:
```bash
sice@sice:~$ ./a.out
sizeof(c)8
funA()
funB()
funA()
对于上面sizeof©==8大家应该不会觉得奇怪,因为C类继承了两个虚函数指针,有疑问的是为什么bb->funB()打印的是funA()呢?这是因为强制类型转化而造成的,见下图,pa,pb指向的虚函数表的结构是一样的,所以将(BaseA*)类型的指针强制转化为(BaseB*)类型的指针属于同类型指针转化是不会出错的,所以pbb指向和pa相同的位置,也就是说因为虚函数表的结构一样, B bb = (B)变成了A bb = (A)a,在C++中我们要忘记这种方式
改进:
int main()
{
C c;
cout<<"sizeof(c)"<<sizeof(c)<<endl;
A *a = &c;
B *b = &c;
B *bb = dynamic_cast<B*>(a);
a->funA();
b->funB();
bb->funB();
}
结果:
sice@sice:~$ ./a.out
sizeof(c)8
funA()
funB()
funB()
这个时候由于使用了dynamic_cast关键字,编译器就会检查a指向的对象是c,发现c的父类有A和B,所以指针强制类型转化是可以通过的,再使bb指向b所指的位置,显然调用了funB()函数,如果说我们遇上需要强制类型转化的类,类里面又定义了虚函数,我们就使用dynamic_cast关键字
在实际的工程中我们采用“单继承加接口的形式”
代码:
#include <iostream>
#include <string>
using namespace std;
class BaseA
{
public:
virtual void funcA()
{
cout << "BaseA::funcA()" << endl;
}
};
class BaseB
{
public:
virtual void funcB()
{
cout << "BaseB::funcB()" << endl;
}
};
class Derived : public BaseA, public BaseB
{
};
int main()
{
Derived d;
BaseA* pa = &d;
BaseB* pb = &d;
BaseB* pbe = (BaseB*)pa; // oops!!
BaseB* pbc = dynamic_cast<BaseB*>(pa);
cout << "sizeof(d) = " << sizeof(d) << endl;
cout << "Using pa to call funcA()..." << endl;
pa->funcA();
cout << "Using pb to call funcB()..." << endl;
pb->funcB();
cout << "Using pbc to call funcB()..." << endl;
pbc->funcB();
cout << endl;
cout << "pa = " << pa << endl;
cout << "pb = " << pb << endl;
cout << "pbe = " << pbe << endl;
cout << "pbc = " << pbc << endl;
return 0;
}