目录
继承的概念
继承的定义
继承类模板
基类和派生类之间的转换
继承中的作用域
派生类的默认成员函数
实现⼀个不能被继承的类
继承与友元
继承与静态成员
单继承,多继承,菱形继承
虚继承
继承和组合
继承的概念
继承机制是面向对象程序设计使代码可以重复利用的一种方法,它允许我们在原有类的基础上,增加新的成员函数(方法)和成员变量(属性),然后可以产生一个新的类,这个类被称为派生类或子类。继承不仅呈现了面向对象程序设计的层次结构,还体现了由简单到复杂的认知过程(因为派生类增添了方法和属性)。
接下来先感受一下继承的使用,

由此可见,老师和学生身份相似性很高,以及生活中有很多方面也会存在这种情况,基于这种代码重复性,可以采用继承了方式来实现,这样可以减少代码的冗余性,以及提高效率。

把所有的公有部分放到一个类(person),让student 和 teacher 都继承它,就可以复用这些成员,提高了编程效率,等到大的项目中,可以很好地感受这一点,而且查找bug时也会更有明确性。
继承的定义
根据上述的例子,person类被称为基类,也可以称为父类,student类 和 teacher类 被称为派生类,也可以被称为子类。

继承方式有三种:public protected private

不同继承方式对子类的影响

1.在派生类中不可见的意思是指,子类中不能直接使用基类的private成员,但是子类会继承基类的private,简单的说,就是基类的private成员无论在什么继承方式下,子类均不能直接调用,但是会继承到子类中,虽然不能直接调用,但是可以间接调用。

2.基类的private成员在派生类中是访问的,如果基类的成员不想再类外被访问,但可以在派生类中可以访问,可以定义为protected成员。类中采用protected修饰的成员和类中采用private修饰的成员的区别就此体现出差异了。
3.对于上面的继承表格,我们很容易发现,除了基类的private成员在派生类中不可见,基类的其他成员在子类中的访问方式 == Min(成员在基类中的访问限定符,继承方式),public > protected > private。
4.使用关键字class定义的派生类,继承基类的默认继承方式为 private ,使用关键字struct定义的派生类,继承基类的默认继承方式为 public。但是在实际项目中,不要这样写,要显示写出继承方式。
例如:

5.在实际运用中一般都是使用public继承,很少使用protected 和 private 继承,在日常的练习中也不提倡使用。因为protected继承下来的public成员只能在派生类中使用,private继承下来的public成员不可以在派生类中使用,实际中很少有这样的继承。
继承类模板

基类和派生类之间的转换
1.public继承的派生类对象 可以赋值给 基类对象的引用或指针或基类。这种方式经常被称为切割或切片,意思为把派生类中基类的成员切出来,基类的引用引用的为派生类中基类的成员,基类的指针指向的为派生类中基类的成员。


格外注意:这里没有发生隐式类型转换。
2.派生类对象可以赋值给基类对象。
3.基类的对象不能赋值给派生类对象。
4.基类的指针或引用可以通过强制类型转换赋值给派生类的指针或引用。
继承中的作用域
继承中的隐藏规则
1.在继承体系中,派生类有派生类的作用域,基类有基类的作用域,两者有着独立的作用域,没有关系。
2.派生类和基类中有同名成员将屏蔽基类的成员,不可以直接访问,但可以指定访问,这种情况构成隐藏关系。
3.成员函数只要函数名相同就构成隐藏。不用在乎返回值,参数列表,函数体。
4.在实际中不推荐基类和派生类定义同名成员。
5.注意由于基类和派生类有着独立的作用域,基类的成员函数和派生类的成员函数不可能构成重载,因为重载的主要前提是在同一个作用域内。



派生类的默认成员函数
刚学习类的时候,大家都接触过了类的默认成员函数,总共有6个默认成员函数,构造函数,拷贝构造函数,赋值运算符重载,析构函数这4个主要的默认成员函数,还有2个几乎不用的默认成员函数,取地址重载,一个是普通对象,一个是const对象。
因为派生类也是类的一种,所以派生类也存在着6个默认成员函数,构造函数一般都需要我们自己来实现,拷贝构造,赋值运算符重载以及析构一般只有向内存的堆上申请空间的情况下,需要我们自己写。
1.派⽣类的构造函数必须调⽤基类的构造函数初始化基类的成员。如果基类没有默认的构造函数,则必须在派⽣类构造函数的初始化列表阶段显⽰调⽤。

2.派生类的拷贝构造函数必须调用基类的拷贝构造完成基类成员的初始化。

3. 派⽣类的operator=必须要调⽤基类的operator=完成基类的复制。需要注意的是派⽣类的 operator=隐藏了基类的operator=,所以需要指定类域显⽰调⽤基类的operator=

注意:如果 自己给自己赋值,不做特殊处理的话,只要有内存堆上的数据,就会导致程序崩溃。
4. 派⽣类的析构函数会在被调⽤完成后⾃动调⽤基类的析构函数清理基类成员。因为这样才能保证派 ⽣类对象先清理派⽣类成员再清理基类成员的顺序。所以我们不必调用基类的析构函数。
5. 派⽣类对象初始化先调⽤基类构造再调派⽣类构造。编译器会把基类的声明放在声明位置的第一个,所以导致了派生类独有的成员的声明在基类的后面,又因为初始化有个原则,先声明的变量先走初始化列表,与初始化列表显示的顺序无关,只与成员的声明顺序有关,所以就会先调用基类构造,可以通过调试或打印来查看。
6. 由于多态中⼀些场景析构函数需要构成重写,重写的条件之⼀是函数名相同。那么编译器会对析构函数名进⾏特殊处理,处理成destructor(),所以基类析构函数不加 virtual的情况下,派⽣类析构函数和基类析构函数构成隐藏关系。
实现⼀个不能被继承的类
方法1:基类的构造函数用private,这样派生类中的构造函数就没有办法调用基类的构造函数,就无法创建一个派生类的对象。
方法2:C++11增添了一个关键字final,如果基类被final所修饰,则就无法被继承,编译器会报错。
这个不能被继承的类,在实际的业务中应该并没有什么用处,在底层的开发中可能有很大用处。

继承与友元
友元关系不能被继承,也就是说基类友元无法访问派生类中的private成员和protected成员。与派生类不存在着任何关系。
继承与静态成员
假如基类中定义了一个静态成员,则整个的继承体系中共用这同一个静态成员。因为它不属于任何对象,只属于类,且全局只存一份在内存的静态区。
单继承,多继承,菱形继承
单继承:一个派生类只有一个直接父类时,这个继承关系被称为单继承。
多继承:一个派生类有两个或两个以上的直接父类时,这个继承关系被称为多继承。多继承对象在内存中的模型是,先继承的基类在前⾯,后⾯继承的基类在后⾯,派⽣类成员在放到最后⾯。声明顺序也是如此,因为声明的顺序本质就是内存,这里要好好理解一下,先继承基类的指针==派生类的指针 != 后继承的基类的指针。
菱形继承:多继承的一种特殊情况。菱形继承是一种非常不好的继承,会导致数据具有二义性以及数据冗余。



虚继承
基于C++的菱形继承,C++就此也提出了虚继承这个概念,它可以解决菱形继承的数据冗余和二义性,但底层非常复杂,会降低性能,所以还是不要写出菱形继承的代码。
可以根据监视窗口来观察
virtual 添加在继承方式前面

继承和组合
1.public继承是⼀种is-a的关系。也就是说每个派⽣类对象都是⼀个基类对象。
2.组合是⼀种has-a的关系。假设B组合了A,每个B对象中都有⼀个A对象。
3.继承允许你根据基类的实现来定义派⽣类的实现。这种通过⽣成派⽣类的复⽤通常被称为⽩箱复⽤ 。术语“⽩箱”是相对可视性⽽⾔:在继承⽅式中,基类的内部细节对派⽣类可⻅。继承⼀定程度破坏了基类的封装,基类的改变,对派⽣类有很⼤的影响。派⽣类和基类间的依 赖关系很强,耦合度⾼。
4.对象组合是类继承之外的另⼀种复⽤选择。新的更复杂的功能可以通过组装或组合对象来获得。对 象组合要求被组合的对象具有良好定义的接⼝。这种复⽤⻛格被称为⿊箱复⽤,因为对象的内部细节是不可⻅的。对象只以“⿊箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使⽤对象组合有助于你保持每个类被封装。
4.优先使⽤组合,⽽不是继承。实际尽量多去⽤组合,组合的耦合度低,代码维护性好。不过也不太那么绝对,类之间的关系就适合继承(is-a)那就⽤继承,另外要实现多态,也必须要继承。类之间的 关系既适合⽤继承(is-a)也适合组合(has-a),就⽤组合。
网硕互联帮助中心


评论前必须登录!
注册