目录
继承(inheritance)是面向对象三大特性之一,继承(inheritance)机制是面向对象程序设计中使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生的新类,称== 派生类(或子类)==,被继承的类称 基类(或父类)。
继承(inheritance)呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程 之前接触的复用都是函数复用, 继承是类设计层次的复用。
1 继承的语法
class 子类 : 继承方式 父类
class BasePage
{
public:
BasePage()
{
}
void header()
{
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left()
{
cout << "Java,Python,C++...(公共分类列表)" << endl;
}
~BasePage()
{
}
};
class JAVA : public BasePage
{
public:
void content()
{
cout << "JAVA-----------------" << endl;
}
};
class PYTHON : public BasePage
{
public:
void content()
{
cout << "PYTHON-----------------" << endl;
}
};
class CPP : public BasePage
{
public:
void content()
{
cout << "CPP-----------------" << endl;
}
};
void Inheritance_basic()
{
//Java页面
cout << "Java下载视频页面如下: " << endl;
JAVA ja;
ja.header();
ja.footer();
ja.left();
ja.content();
//PYTHON页面
cout << "PYTHON下载视频页面如下: " << endl;
PYTHON py;
py.header();
py.footer();
py.left();
py.content();
cout << "--------------------" << endl;
//CPP页面
cout << "CPP下载视频页面如下: " << endl;
CPP cp;
cp.header();
cp.footer();
cp.left();
cp.content();
}
结果:
2 继承方式
总结:
1、基类private成员无论以什么方式继承到派生类中都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。
2、基类private成员在派生类中不能被访问,如果基类成员不想在派生类外直接被访问,但需要在派生类中访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
3、上面的表格看起来复杂,实际上归纳一下就会发现:基类的私有成员在子类都是不可见;基类的其他成员在子类的访问方式就是访问限定符和继承方式中权限更小的那个(权限排序:public>protected>private)。
4、使用关键字class时默认的继承方式是private;
使用struct时默认的继承方式是public,但最好显式地写出继承方式。
5、在实际使用时一般都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,扩展和维护性不强。
class Base1
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1 :public Base1
{
public:
void func()
{
m_A = 10; //父类总的公共成员,子类中依旧为公共
m_B = 20; //父类总的保护成员,子类中依旧为保护
//m_C = 20; //父类总的私有成员,子类访问不到
}
};
class Son2 :protected Base1
{
public:
void func()
{
m_A = 10; //父类总的公共成员,子类中为保护
m_B = 20; //父类总的保护成员,子类中依旧为保护
//m_C = 20; //父类总的私有成员,子类访问不到
}
};
class Son3 :private Base1
{
public:
void func()
{
m_A = 10; //父类总的公共成员,子类中为私有
m_B = 20; //父类总的保护成员,子类中为私有
//m_C = 20; //父类总的私有成员,子类访问不到
}
};
void Inheritance_From_Base1()
{
Son1 s1;
s1.m_A = 123;
//s1.m_B = 234;//err,在Son1类中,为保护权限,不可类外访问
Son2 s2;
//s2.m_A = 123; //err,在Son2类中,为保护权限,不可类外访问
//s2.m_B = 234; //err,在Son2类中,为保护权限,不可类外访问
Son3 s3;
//s3.m_A = 123; //err,在Son3类中,为私有权限,不可类外访问
//s3.m_B = 234; //err,在Son3类中,为私有权限,不可类外访问
}
3 继承中的对象模型
问题:从父类继承过来的成员,哪些属于子类对象中?
class Base2
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class MySon : public Base2
{
public:
int m_D;
};
void Inheritance_From_Base2()
{
cout << "Size of Father : " << sizeof(Base2) << endl;
cout << "Size of Son : " << sizeof(MySon) << endl;
}
结果:
利用VS工具查看类布局:
父类中非静态的成员属性都会被子类继承
父类中的 私有成员在继承的时候也是被继承了,但无法访问,是被编译器隐藏了
4 继承中构造和析构顺序
class Base
{
public:
Base()
{
cout << "父类 Base 的 构造函数!!" << endl;
}
~Base()
{
cout << "父类 Base 的 析构函数!!" << endl;
}
};
class MySon :public Base
{
public:
MySon()
{
cout << "子类 MySon 的 构造函数!!" << endl;
}
~MySon()
{
cout << "子类 MySon 的 析构函数!!" << endl;
}
};
void Inheritance_From_Base()
{
MySon s;
}
结果:
Q: 父类和子类的构造和析构顺序是谁先谁后?
A: (1)子类继承父类后,当创建子类对象,也会调用父类的构造函数
(2)继承中的构造和析构顺序:
构造顺序: 父类构造—> 子类构造
析构顺序: 子类析构—> 父类析构
5 继承同名成员处理方式
class Base
{
public :
Base()
{
m_A = 123;
}
void Func()
{
cout << "Base -- Func()函数调用" << endl;
}
void Func(int a)
{
cout << "Base -- Func(int a)函数调用" << endl;
}
public:
int m_A;
};
class MySon : public Base
{
public:
MySon()
{
m_A = 789;
}
void Func()
{
cout << "Son -- Func()函数调用" << endl;
}
public :
int m_A;
};
void Inheritance_From_Base()
{
MySon s;
cout << "子类 中的 m_A 为: " << s.m_A << endl;
cout << "父类 中的 m_A 为: " << s.Base::m_A << endl;
s.Func(); //子类直接调用的时候,会掉自己的成员函数
s.Base::Func(); //加作用域,调用父类中同名函数
//s.Func(100); //ERR, 子类会把父类中 所有的 Func()名字的函数都屏蔽掉
s.Base::Func(100); //OK
}
结果:
结论:
1、当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数;
2、 当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
A: (1)访问子类同名成员 ----> 直接访问即可
(2)访问父类同名成员 ----> 需要加作用域
6 继承同名静态成员处理方式
class Base
{
public:
static void Func()
{
cout << "父类下的 静态成员函数 " << endl;
}
static int m_A;
};
int Base::m_A = 123;
class MySon : public Base
{
public:
static void Func()
{
cout << "子类下的 静态成员函数 " << endl;
}
static int m_A;
};
int MySon::m_A = 456;
6.1 通过对象访问不同类中的值
void Inheritance_From_Base()
{
MySon s1;
cout << "通过对象访问 静态成员变量:" << endl;
cout << "子类 中的 m_A 为: " << s1.m_A << endl; //子类下面的值:456
cout << "父类 中的 m_A 为: " << s1.Base::m_A << endl; //父类下面的值:123
cout << "通过对象访问 静态成员函数 :" << endl;
MySon s2;
s2.Func();
s2.Base::Func();
}
6.2 通过类名访问
void Inheritance_From_Base5()
{
cout << "通过类名访问 静态成员变量:" << endl;
cout << "子类 中的 m_A 为: " << MySon::m_A << endl;
cout << "父类 中的 m_A 为: " << MySon::Base::m_A << endl; //第一个 :: 代表通过类名方式访问。第二个 :: 表示访问父类作用于下的某个静态成员
cout << "通过类名访问 静态成员函数 :" << endl;
MySon5::Func();
MySon5::Base5::Func(); //出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问
}
Q: 继承中同名的静态成员在子类对象上如何进行访问?
A: 静态成员和非静态成员出现同名,处理方式一致
访问子类同名成员 直接访问即可
访问父类同名成员 需要加作用域
总结:同名静态成员处理方式和非静态处理方式一样,只不过有两种访问的方式(通过对象 和 通过类名)
7 多继承语法
C++允许一个类继承多个类
class 子类 :继承方式 父类1 , 继承方式 父类2…
多继承可能会引发父类中有同名成员出现,需要加作用域区分
class Base6
{
public:
Base6()
{
m_A = 100;
}
int m_A;
};
class Base7
{
public:
Base7()
{
m_A = 200;
}
int m_A;
};
class MySon : public Base6, public Base7
{
public :
int m_C;
int m_D;
};
void Inheritance_From_Base6()
{
MySon s1;
cout << "MySon 的大小: " << sizeof(MySon) << endl;
//当不同的父类中 出翔相同的成员变量,需要加作用域区分
cout << "父类 Base6::m_A = " << s1.Base6::m_A << endl;
cout << "父类 Base7::m_A = " << s1.Base7::m_A << endl;
}
8 菱形继承
概念:
两个派生类继承同一个基类;又有某个类同时继承者两个派生类
菱形继承 有 相同的数据有多份,浪费资源
利用虚继承解决菱形继承问题:在继承之前,加上关键字 virtual 变为虚继承,此时 父类 称为 虚基类
class Animal
{
public:
int m_Age;
};
class Sheep : virtual public Animal //利用虚继承解决菱形继承问题
{
};
class Tuo : virtual public Animal //利用虚继承解决菱形继承问题
{
};
class SheepTuo :public Sheep, public Tuo
{
};
void Inheritance_From_Base()
{
SheepTuo st;
st.Sheep::m_Age = 18;
st.Tuo::m_Age = 28;
//当菱形继承时,通过不同作用域 访问 不同父类中的成员变量
cout << "Sheep: AGE = " << st.Sheep::m_Age << endl;
cout << "Tuo: AGE = " << st.Tuo::m_Age << endl;
cout << "SheepTuo: AGE = " << st.m_Age << endl;
}