继承是面向对象三大特性之一
有些类与类之间存在特殊关系:例如:
动物——(猫、狗…)———(加菲猫、波斯猫…哈士奇、京巴…)
下级别的成员除了拥有上一级的共性,还有自己的特性;
这时,我们就可以考虑利用继承的技术,减少重复代码;
继承的基本语法:
例如:好比我们平时看到的很多网站中,有公共的头部、公共的地步;而其他子页面只有重心内容不同;
这时就可以利用普通写法和继承的写法来实现网页中的内容;
继承的好处:减少重复代码;
语法:class 子类 :继承方式 父类{ }
子类 也称为 派生类;
父类 也称为 基类;
- 例如:
定义一个父类:
class BasePage{
public:
void header(){}
void footer(){}
void left(){}
}
语法:
class A :public B;
A类称为子类 或 派生类;
B类称为父类 或 基类;
定义Java页面、C++页面:
class Java : public BasePage{
public :
void content(){ cout<<"JAVA";}
}
class C++ : public BasePage{
public :
void content(){ cout << "c++"; }
}
派生类中的成员,包含两大部分:
一类是从基类继承过来的,一类是自己增加的成员;
从基类继承过来的表现其共性,而新增的成员体现其个性;
继承方式
继承的语法:class 子类 :继承方式 父类
继承方式一共有三种:
- 公共继承–public
- 保护继承–protected
- 私有继承–private
注意:
无论是public共有继承,protected保护继承,还是private私有继承都不可以访问父类的私有属性;
公有继承:
- 父类中的公共权限成员,到子类中依然是公共权限;
- 父类中的保护权限成员,到子类中依然是保护权限;
- 父类中的私有权限成员,子类访问不到;
回顾三大权限的用法:
- 类在设计时,可以把属性和行为放在不同的权限下,加一控制;
访问权限有3种:
1.public——公共权限:类内可以访问,类外也可以访问
2.protected——保护权限:类内可以访问,类外不可以访问
3.private——私有权限:类内可以访问,类外不可以访问
**保护权限与私有权限的区别:
保护权限:protected:儿子可以访问父亲中的保护内容;
私有权限:private儿子不可以访问父亲的私有内容;**
保护继承:
- 父类中的公共权限和保护权限到子类中都转为保护权限;
- 父类中的私有权限,子类访问不到;
· 所以保护继承继承到的除私有属性外都为保护权限,类外无法访问;**
私有继承: - 父类中的所有非静态成员到子类中变为私有成员;
- 所以私有继承继承到的父类成员在类外都访问不到;
继承中的对象模型:
- 父类中的所有非静态成员属性都会被子类继承下去;
- 父类中的私有成员属性 是被编译器给隐藏了,因此访问不到,但确实被继承下去了;
继承中构造和析构的顺序
- 子类继承父类后,当创建子类对象时,同时也会调用父类的构造函数(即先有父类);
问题:父类和子类的构造和析构顺序是谁先谁后?
继承中的构造和析构顺序如下:
1.先构造父类,再构造子类;(即先有父类再有子类,先调用base的构造,再调用son的构造)
2.析构的顺序与构造的顺序相反;(即先调用son的析构,再调用base的析构)
继承同名成员处理方式:
- 问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
1.访问子类同名成员,直接访问即可;
2.访问父类同名成员,需要加作用域;
class Base {
public:
Base() {
m_A = 100;
}
int m_A;
};
class Son :public Base {
public:
Son() {
m_A = 200;
}
int m_A;
};
int main() {
Son s;
cout << "Son下的 m_A=" << s.m_A << endl;
如果通过子类对象 访问到父类中同名成员,需要加作用域;
cout << "Base下的 m_A=" << s.Base::m_A << endl;
system("pause");
return 0;
}
- 同名函数:正常情况下(与同名成员变量一样)
class Base {
public:
void func() {
cout << "Base下的func" << endl;
}
void func(int a) {
cout << a<<endl;
}
};
class Son :public Base {
public:
void func() {
cout << "son下的func" << endl;
}
};
int main() {
Son s;
正常使用下同名成员变量一样
s.func();
s.Base::func();
当出现同名函数重载时:
如果子类中出现和同名的成员函数,子类的同名成员会隐藏掉父类中所有父类中所有同名成员函数;
如果想访问到父类中被隐藏的同名成员函数,需要加作用域;
//s.func(100);————不可使用,因为子类中没有有参数的func()函数
s.Base::func(100);
system("pause");
return 0;
}
补加:
3.当子类中出现和同名的成员函数,子类的同名成员会隐藏掉父类中所有父类中所有同名成员函数,加作用域可以访问到父类中同名函数;
继承同名静态成员处理方式:
- 问题:机场中同名的静态成员在子类对象上如何进行访问?
静态成员和非静态成员出现同名,处理方式一致
1.访问子类同名成员,直接访问即可;
2.访问父类同名成员,需要加作用域;
补充——静态成员变量可以通过类名方式进行访问:
即有两种访问方式:通过对象和通过类名;
cout<<"Son::m_A"<<Son::m_A;
cout<<"Son::m_A"<<Son::Base::m_A;
另外:子类出现和父类同名静态成员函数,也会隐藏父类中所有同名成员函数;
如果想访问父类中杯隐藏同名成员,需要加作用域:
Son::Base::func(100);
回顾静态变量:
- 静态成员就是在成员变量和成员函数前加上关键字static,称之为静态成员;
静态成员分为静态成员变量和静态成员函数;
静态成员变量:
- 所有对象共享同一份数据;
- 在编译阶段分配内存;
- 类内声明,类外初始化;
class Base{
//静态成员变量
static int m_A;
}
//静态成员变量是在类内声明,类外初始化
int Person::m_A = 0;
注意:类外初始化的时候一定要加上作用域,不然无法初始化;
静态成员函数不可以访问非静态成员变量
因为非静态成员变量会指定为那个对象的属性值,所以直接使用不知道是那个对象的属性值
而静态成员函数为公用,且静态成员变量为也为所有对象共享同一份数据;
所以静态成员函数只能访问静态成员变量
多继承语法
C++允许一个类继承多个类;
语法: class 子类 :继承方式 父类1,继承方式 父类2{ }
多继承中可能会引发父类中有同名成员出现,需要加作用域区分;所以C++实际开发中不建议用多机场;
菱形继承:
菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义;
所以需要利用虚继承来解决菱形继承问题;
.
利用虚继承 解决菱形继承的问题;
继承之前 加上关键字 virtual 变为虚继承;
Animal类称为 虚基类;
利用对象模型进行查看:
虚继承相当于运用指针(或引用的方法),所以继承的子类使用虚继承则是只在一块内存上进行操作;
vbptr :
v——virtual (英 /ˈvɜːtʃuəl/ )虚的,虚拟的;
b——base
ptr——pointer;( /ˈpɔɪntə®/ 指针)