概念
面向对象设计时,复用之前类,并在其基础上进行扩展,被继承的类叫做基类(父类),继承的类叫做派生类(子类)
继承格式
class Person
{
protected:
int _age;
string _name;
};
class Student : public Person
{
protected:
int _id;
};
继承方式与访问限定符
继承后基类成员的访问权限
这里在派生类中不可见,并不是没有被派生类继承,而是被继承了,但是不能够访问
基类和派生类赋值转换
派生类可以给基类的对象/基类的引用/基类的指针赋值,就像切片或者切割过去
子类可以给父类赋值
父类不能给子类赋值,否则就会报错
继承的作用域
继承中基类跟派生类是两个作用域
当两者中出现同名成员时,派生类就会屏蔽基类中的,这种情况叫做隐藏
如果想要访问,可以加上域作用限定符 基类::基类成员
注意:因为重载的概念是在同一作用域中,而基类和派生类是两个作用域,所以不会构成重载
一般不建议使用同名成员
如下代码中是隐藏容易出现的问题
因为父类f()函数被子类隐藏,所以到调用时加上参数,就无法调用父类中的函数,并且子类中的函数有参数,所以如下代码会报错 函数调用参数太少
class A
{
public:
void f()
{
cout << "A::f()" << endl;
}
int a;
};
class B : public A
{
public:
void f(int a)
{
cout << "B::f()" << endl;
}
int a;
};
int main()
{
B b;
b.f();
return 0;
}
派生类默认构造函数
派生类构造函数需要调用基类构造函数来初始化基类中的那部分,如果基类中没有默认构造函数,就需要在派生类中初始化列表显示调用
派生类拷贝构造函数和operator=都需要分别调用基类中的拷贝构造函数和operator=
派生类的析构函数只需要释放派生类中的数据,之后自动调用基类的析构,这样才能保证先析构派生类,然后析构基类
注意:在构造时,先调用基类构造,然后再调用派生类构造函数
在析构时,先调用派生类析构,然后再调用基类析构
继承和友元
父类友元不是子类的友元,友元不能被父类继承
如果想要友元在子类中被访问就需要在子类中说明友元
继承和静态成员
基类中static成员,无论有多少派生类,始终只有一个静态成员
基类的对象中不包含基类的静态成员变量,静态成员变量是类的成员
菱形继承与菱形虚拟继承
单继承:一个子类只有一个父类直接关系
多继承:一个子类有两个或者多个父类直接关系
菱形继承
菱形继承存在二义性和数据冗余的问题
因为Assistant的对象中继承Student跟Teacher就会有两个Person,就会出现问题
虚拟继承就能够解决这个问题
class Person
{
protected:
string _name;
};
class Student : virtual public Person
{
protected:
int _id;
};
class Teacher : virtual public Person
{
protected:
int _num;
};
class Assistant : public Teacher, public Student
{
protected:
string major;
};