知识的学习在于点滴记录,坚持不懈;知识的学习要有深度和广度,不能只流于表面,坐井观天;知识要善于总结,不仅能够理解,更知道如何表达!
继承简介
继承的好处是什么?
1.基类给所有派生类提供公共的属性(成员变量)和方法(成员函数),通过继承达到代码复用的目的。
2.基类可以给所有派生类提供统一的纯虚函数接口,派生类通过函数重写,达到多态调用的目的。(OOP的很多设计模式离不开继承和多态,为了达到良好的软件设计,如高内聚,低耦合,遵循‘开-闭’原则等,继承和多态是必须涉及到的)。
OOP面向对象语言如C++,Java,Python,PHP等,非常重要的一项语言特征就是继承,继承首先可以做到代码的复用,派生类通过继承基类,可以复用基类的成员变量和成员方法,那么这里的第一个问题就是派生类从基类继承了那么多成员,其访问限定是怎么样的呢?如下代码示例:
// A是基类
class A
{
public:
int ma;
protected:
int mb;
private:
int mc;
};
// B是派生类,继承到派生类里面的成员访问限定要小于等于继承方式
class B : public A
{
public:
int md;
protected:
int me;
private:
int mf;
};
派生类B从基类A继承了成员ma,mb和mc,那么这三个成员在派生类B种的访问权限是什么,看如下表格:
继承方式 | 基类成员访问限定 | 在派生类种的访问限定 | 外部函数中的访问限定 |
---|---|---|---|
public | public | public | 在外部可以访问 |
public | protected | protected | 在外部不可以访问 |
public | private | 派生类中不可见 | 在外部不可以访问 |
继承方式 | 基类成员访问限定 | 在派生类种的访问限定 | 外部函数中的访问限定 |
---|---|---|---|
protected | public | protected | 在外部不可以访问 |
protected | protected | protected | 在外部不可以访问 |
protected | private | 派生类中不可见 | 在外部不可以访问 |
继承方式 | 基类成员访问限定 | 在派生类种的访问限定 | 外部函数中的访问限定 |
---|---|---|---|
private | public | private | 在外部不可以访问 |
private | protected | private | 在外部不可以访问 |
private | private | 派生类中不可见 | 在外部不可以访问 |
从上面的总结可以看出:
1.基类的private私有成员,无论采用什么继承方式,在派生类里面都是可以继承下来,但是无法访问。
2.protected和private的区别。基类的private私有成员,在派生类和外部都不能直接访问;但是基类protected的成员,在派生类中是可以访问的,在外部不能访问。
派生类对象的构造过程
派生类从基类继承的成员变量该如何初始化呢?必须通过调用基类的构造函数来初始化从基类继承来的成员!,如下代码示例:
#include <iostream>
using namespace std;
class Base
{
public:
Base(int data) :ma(data) {
cout << "Base()" << endl; }
~Base() {
cout << "~Base()" << endl; }
protected:
int ma;
};
class Derive : public Base
{
public:
// 必须通过调用基类的构造函数来初始化从基类继承来的成员
Derive(int data) :Base(data) {
cout << "Derive()" << endl; }
~Derive() {
cout << "~Derive()" << endl; }
private:
int mb;
};
int main()
{
Derive d(10);
return 0;
}
代码运行打印如下:
Base()
Derive()
~Derive()
~Base()
可以看到,一个派生类对象的构造和析构顺序是:
1.先调用基类构造函数,构造从基类继承来的成员
2.再调用派生类自己的构造函数,构造派生类自己的成员
3.先调用派生类自己的析构函数,释放派生类自己占用的外部资源
4.调用基类的析构函数,释放基类部分成员占用的外部资源
重载、隐藏、覆盖
基类和派生类中,是否可以定义同名的成员(包括成员变量和成员方法名字)呢?答案是可以的。可以这样理解,派生类从基类继承来的成员,都带有基类的作用域,作用域不同,名字相同的成员是不会冲突的。
基类和派生类之间的同名成员方法有三种关系:重载,隐藏和覆盖。如下代码示例:
#include <iostream>
using namespace std;
class Base
{
public