C++中的继承
一:继承是什么
1.继承的概念:继承的机制是面向对象设计使代码复用的重要手段,他可以使程序员在原有类的基础上对其进行扩展,增加功能产生新的类,叫派生类(子类)。
2.继承的定义
①:格式
例如:class student : public teacher
上述例子中,冒号前的student为派生类(子类),冒号后的public为继承的方式,teacher为基类(父类)。
②:继承方式和区别
继承方式一共有三类,①中展现了的是public的继承方式,另外两个方式分别是protected,private两种继承方式。
区别:
类成员/继承方式 | public继承 | protected继承 | private继承 |
---|---|---|---|
public成员 | public | protected | private |
protected成员 | protected | protected | private |
private成员 | 在派类中不可见 | 在派类中不可见 | 在派类中不可见 |
1.在继承中均是以变小的方式继承,且public>protected>private;
2.private的成员被继承时均是不可见的,是因为基类的private成员在派生类中不管是类外还是类里不可以去访问基类的私有成员,但是本质上还是被继承了过来。
3.由于private成员不可以被访问,但是如果基类的private成员要是想在派生类里面被访问那就可以让其成为protected成员。(类里面表示的是在派生类的里面去调用)
二:继承中有什么规则
1.基类和派生类的赋值
①:派生类对象可以赋值给基类的对象、基类对象的指针、基类对象的引用。
②:基类对象不可以赋值给派生类对象。
其中,赋值的原因大致可以如图所示:
图中可以表现出,由于派生类是在基类基础上进行增加的,所以派生类可以给基类赋值的原因是派生类由基类的所有成员,并且赋值也是通过类似切割的方式进行赋值的。
2.继承中的作用域
①:在继承中,基类和派生类有这自己的独立的作用域。
②:基类和派生类有同名函数时,派生类会对基类的同名函数进行隐藏。(只要是同名,不管是重载函数,都会全部隐藏)
③:隐藏的意思是由派生类生成的对象,调用同名函数时,只能调用派生类的该函数。
3.派生类的默认构造函数
①:派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员,如果基类没有构造函数,则必须在派生类构造函数的初始化阶段表现出来。
②:派生类的拷贝构造函数必须调用基类的拷贝构造函数进行基类拷贝初始化。
③:派生类的赋值语句必须调用基类的赋值语句完成基类的赋值。
④:基类和派生类的析构函数和构造函数的顺序如下:
class A
{
public:
A()
{
cout<<"A的初始化"<<endl;
}
~A()
{
cout<<"A的析构"<<endl;
}
};
class B:public A
{
public:
B()
{
cout<<"B的初始化"<<endl;
}
~B()
{
cout<<"B的析构"<<endl;
}
};
其结果如下
那么初始化的顺序是先构造基类,再构造派生类,反过来便是析构的顺序。
(如果有多个基类的时候冒号后的顺序为基类的构造顺序)
4.继承与友元函数和静态成员
①:友元关系不能继承,如基类的友元函数可以访问基类的私有成员,则该友元函数不可以访问派生类的私有成员。
②:如果基类定义了静态成员,那么不管如何继承,这个体系中静态成员只有一个。
三:关于菱形继承
1.菱形继承的由来
①:单继承为一个派生类类只有一个基类的继承,例如:
表示为单继承,其中student为teacher的派生类,teacher为person的派生类。
②:多继承为一个派生类有多个基类,例如:
如图,son为基类father和基类mother的派生类。
③:菱形继承为多继承的一种特例,例如:
如图,son为基类father和基类mother的派生类,father和mother分别又为person的派生类。
由于图像特别像菱形,所以成为菱形继承。
2.菱形继承的问题及解决办法
①:问题:由于多继承中派生类的基类类最终为一个基类,则就会产生继承的二义性以及冗余代码。
class A
{
public:
int m_a;
};
class B : public A
{
public:
int m_b;
};
class C : public A
{
public:
int m_c;
};
class D : public B, public C
{
public:
int m_d;
};
int main()
{
D d;
cout << d.m_a << endl;
cout << d.m_b << endl;
cout << d.m_c << endl;
cout << d.m_d << endl;
}
如上述代码,如果运行的化,必然会出现错误,因为当d去访问m_a的时候,它并不知道是B中的还是C中的,所以便产生了二义性,也就产生了问题。
②:如何解决菱形问题中的二义性和冗余代码
使用虚拟继承,即在继承相同的基类时使用virtual去修饰,例如
class A
{
public:
int m_a;
};
class B : virtual public A
{
public:
int m_b;
};
class C : virtual public A
{
public:
int m_c;
};
class D : public B, public C
{
public:
int m_d;
};
int main()
{
D d;
cout << d.m_a << endl;
cout << d.m_b << endl;
cout << d.m_c << endl;
cout << d.m_d << endl;
}
此时代码便可以运行下来。