一、单继承&多继承&菱形继承
单继承与多继承
单继承:一个子类只有一个直接父类时称这个继承关系为单继承
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
菱形继承
菱形继承引发的问题:
数据冗余
例:B、C中的_a都是来自于A,其实两个_a是同一个,但是因为B、C各自继承,产生了两份,造成了数据冗余
二义性
例:由于D继承于B、C,B、C继承于A,所以D中的_a不知道是来自B还是来自C,于是产生了二义性。
二、虚继承-------解决菱形继承的数据冗余与二义性
将B和C的继承变为虚继承:
class B:virtual public A
class C:virtual public A
不过虚继承只能解决这种有公有继承祖先的多继承,菱形继承,比如下面这个例子加上virtual就无法解决
class A1
{
public:
int a;
};
class A2
{
public:
int a;
};
class B :virtual public A1, virtual public A2
{
public:
};
int main()
{
B b1;
b1.a;//编译出错,访问不明确
system("pause");
return 0;
}
这样我们只能自己解决,加上域限制符:
int main()
{
B b1;
b1.A1::a=100;
b1.A2::a=200;
system("pause");
return 0;
}
问题:
在菱形继承中加上virtual和不加virtual的父类大小有区别吗?
class A
{
public:
int z;
};
class base1:virtual public A//加上virtual
{
public:
int a;
};
class base2:public A//没有加virtual
{
public:
int b;
};
class stu:public base1,public base2
{
public:
int c;
};
int main()
{
cout << sizeof(A) << endl;
cout << sizeof(base1) << endl;//加上virtual
cout << sizeof(base2) << endl;//没有加virtual
system("pause");
return 0;
}
//结果为:4 12 8
由此分析:加上virtual后,编译器给变量增加了一个特殊的属性
三、虚函数
类的成员函数前面加virtual关键字,则这个成员函数称为虚函数。
虚函数重写:当在子类的定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写(也称覆盖)了父类的这个虚函数。
重写只发生在父类与子类之间。
class Person
{
public :
virtual void BuyTickets()
{
cout<<" 买票"<< endl;
}
protected :
string _name ; // 姓名
};
class Student : public Person
{
public :
virtual void BuyTickets()
{
cout<<" 买票-半价 "<<endl ;
}
protected :
int _num ; //学号
};
//void Fun(Person* p)
void Fun (Person& p)
{
p.BuyTickets ();
}
void Test ()
{
Person p ;
Student s ;
Fun(p );
Fun(s );
}
简单理解多态:
1.指向父类调父类,指向子类调子类。
2.构造函数不能为虚函数。
3.最好把父类的析构定义为虚函数,子类将重写父类的虚函数。
4.父类是虚函数,子类不是虚函数,但是子类继承了父类的属性,可以构成多态。
四、纯虚函数
在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚 函数在派生类中重新定义以后,派生类才能实例化出对象。
class Person
{
virtual void Display () = 0; // 纯虚函数
protected :
string _name ; // 姓名
};
class Student : public Person
{};
重载、重写、重定义