单继承&多继承
一个子类只有一个直接父类时称这种继承关系为单继承。
一个子类有两个或者两个以上的父类时称这种继承关系为多继承。
菱形继承 ---------特殊的多继承
有很大的缺点:
- 二义性
如上图,D中会有两份相同的数据,本质都来源于A,在调用派生类里面的这个数据时就会出现问题时就会出现二义性问题。 - 数据冗余
D有两个父类,则数据会很多,甚至有重复的,造成数据冗余
解决办法一:加作用域可以解决二义性问题
class Person
{
public:
string _name ; // 姓名
};
class Student : public Person
{
protected :
int _num ; //学号
};
class Teacher : public Person
{
protected :
int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :
string _majorCourse ; // 主修课程
};
void Test ()
{
// 显示指定访问哪个父类的成员
Assistant a ;
a.Student ::_name = "xxx";
a.Teacher ::_name = "yyy";
}
解决办法二:虚继承----解决菱形继承的二义性问题和数据冗余问题
虚继承解决二义性与数据冗余的原理
先看这个代码
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;//没加virtual时,这两个a的地址不同
d.b = 3;
d.c = 4 ;
d.d = 5;
}
查看两个a的内存,结果就是B,C里面的a内存地址单元不同
d中其他数据:
虚继承以后,B,C中的a的内存单元只有一个,值为2,内存单元中的值先为1,其后变为2
class A
{
public:
int a;
};
class B:virtual public A //虚继承
{
public:
int b;
};
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;
}
虚函数解决二义性与数据冗余的本质----虚基表
所以虚继承的特点是虚基类共享同一个对象地址,节省了空间,也避免了数据冗余以及二义性的问题。
lass A
{
public:
void set_a(int m)
{
a = m;
}
int a ;
};
class B :public A
{
public:
void set_b(int n)
{
b = n;
}
int b;
};
class C:public A
{
public:
void set_c(int n)
{
c = n;
}
int c;
};
class D :public B,public C
{
public:
void set_d(int n)
{
d = n;
}
int d;
};
int main()
{
D d;
d.set_a(200); //a在类B和类C中都有,所以报错,调用不明确
d.A::set_a(200); //加上作用域可以解决这个问题
d.set_b(300);
d.set_c(400);
d.set_d(500);
system("pause");
return 0;
}
//使用虚基类
class A
{
public:
void set_a(int m)
{
a = m;
cout << a << endl;
}
int a ;
};
class B : virtual public A //虚基类
{
public:
void set_b(int n)
{
b = n;
cout << b << endl;
}
int b;
};
class C: virtual public A //虚基类
{
public:
void set_c(int n)
{
c = n;
cout << c << endl;
}
int c;
};
class D :public B,public C
{
public:
void set_d(int n)
{
d = n;
cout << d << endl;
}
int d;
};
int main()
{
D d;
d.set_a(200); //设置虚基类类后不会报错
d.set_b(300);
d.set_c(400);
d.set_d(500);
cout << d.a << endl;
system("pause");
return 0;
}
因此菱形继承也会产生二义性和数据冗余的问题的问题,所以尽量不要设计菱形继承,否则就需要用菱形虚继承来解决问题
虚继承和直接继承有什么区别?
- 时间上:派生类访问虚继承的所用的时间更久,因为这是通过某种引用来完成的,增加了寻址功能。其实就是调整this指针指向虚基类对象。
- 空间上:由于共享所以不必在对象内存中保存多份虚基类对象的拷贝,这样比直接继承省空间。
继承于static成员函数
基类定义了一个(static)静态成员,派生类可以继承,实际上来说整个继承体系中都只有一个static成员,无论有多少个派生类,派生类和父类中的static共用空间。
class base
{
public:
static int a;
};
int base::a = 100;
class devired :public base
{
public:
int d;
};
int main()
{
devired d;
cout<<d.a<<endl; //结果为100
system("pause");
}
继承与友元
在C++中,派生类把基类中所有的成员继承过来,除了构造函数和析构函数。
友元函数不属于类,它只是给类开了一个后门
本来类外不可以访问类的私有成员,通过友元函数就可以
所以不能继承。子类继承父类,那么默认的,就是继承了父类的成员函数和成员变量。