继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。
eg:学生是属于人,可以继承人的一些属性和方法,同时还有自己的私有属性。
class Person //基类
{
public:
Person(string name, int age) :m_age(age),m_name(name)
{
}
void Print()
{
cout << m_name << " : " << m_age << endl;}
public:
string m_name ;
int m_age ;
};
//继承
class Student : public Person //Student为派生类
{
public:
Student(string name, int age,int scores=0):Person(name, age), math_scores(scores)
{
}
private:
int math_scores;
};
void main()
{
Student s("张三疯", 20);
s.Print();
}
继承方式:public/private/protected继承
先说一下public/private/protected这三个的限定符的含义和区别:
public,private,protected的区别,继承方法与访问权限
1、public:public表明该数据成员、成员函数是对所有用户开放的,所有用户都可以直接进行调用
2、private:private表示私有,私有的意思就是除了class自己之外,任何人都不可以直接使用,即便是子女,朋友,都不可以使用。
3、protected:protected对于子女、朋友来说,就是public的,可以自由使用,没有任何限制,而对于其他的外部class,protected就变成private。
class A
{
public:
A() {
cout << "A::A()" << endl; }
~A() {
cout << "A::~A()" << endl; }
int a;
};
class B : public A
{
public:
B() {
cout << "B::B()" << endl; }
~B() {
cout << "B::~B()" << endl; }
int b;
};
void main()
{
B b;
}
友元关系不能继承(友元函数没有传递性),也就是说基类友元不能访问子类私有和保护成员。
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。
单继承:一个子类只有一个直接父类时称这个继承关系为单继承
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
菱形继承:菱形继承是多继承的一种特殊情况
由于Teacher 和 Student分别继承了Person,Person里的成员在Teacher 和 Student各有一份拷贝,当Assistant继承了Teacher 和 Student之后,就相当将Person的成员拷贝了两份,这会造成数据冗余和调用时的二义性问题。
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._name = "peter";
// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";
}
对于二义性问题在使用时可以加上作用域限定来解决,但是还是会有数据冗余,怎么解决?
虚拟继承:(加关键字virtual,eg:virtual public Person)可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
class Person
{
public :
string _name ; // 姓名
};
class Student : virtual public Person
{
protected :
int _num ; //学号
};
class Teacher : virtual public Person
{
protected :
int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :
string _majorCourse ; // 主修课程
};
void Test ()
{
Assistant a ;
a._name = "peter";
}
设置虚继承之后,通过当前类中的虚基表里面存放的偏移地址来找到继承于Person的成员,而不是直接将Person的成员拷贝过来,这样就解决数据冗余的问题。
一般不要设计出菱形继承,否则在复杂度及性能上都有问题