C++ 继承与派生,继承性是面向对象程序设计最重要的特征,可以说,如果没有掌握继承性,就等于没有掌握类和对象的精华,就是没有掌握面向对象程序设计的真谛
继承的概念
继承是类与类之间的关系,是一个很简单很直观的概念,与现实世界中的继承类似,例如儿子继承父亲的财产;
继承(Inheritance)可以理解为一个类从另一个类获取成员变量和成员函数的过程;
例如类 B 继承于类 A,那么 B 就拥有 A 的成员变量和成员函数;被继承的类称为父类或基类,继承的类称为子类或派生类;(所以基类与派生类,父类与子类说的都是继承
)
派生类除了拥有基类的成员,还可以定义自己的新成员,以增强类的功能;
以下是两种典型的使用继承的场景:
-
当你创建的新类与现有的类相似,只是多出若干成员变量或成员函数时,可以使用继承,这样不但会减少代码量,而且新类会拥有基类的所有功能;
-
当你需要创建多个类,它们拥有很多相似的成员变量或成员函数时,也可以使用继承;可以将这些类的共同成员提取出来,定义为基类,然后从基类继承,既可以节省代码,也方便后续修改成员;
下面,我们定义了一个基类 People,然后由此派生出 Student 类:
#include <cstdio>
using namespace std;
class People {
public:
void setname(const char *name) { m_name = name; }
void setage(int age) { m_age = age; }
const char *getname() const { return m_name; }
int getage() const { return m_age; }
private:
const char *m_name;
int m_age;
};
class Student : public People {
public:
void setscore(float score) { m_score = score; }
float getscore() const { return m_score; }
private:
float m_score;
};
int main() {
Student s;
s.setname("Otokaze");
s.setage(18);
s.setscore(111);
printf("name: %s, age: %d, score: %g\n", s.getname(), s.getage(), s.getscore());
return 0;
}
本例中,People 是基类,Student 是派生类。Student 类继承了 People 类的成员,同时还新增了自己的成员变量 m_score 和成员函数 setscore()、getscore();这些继承过来的成员,可以通过子类对象访问,就像自己的一样;
class Student : public People
class 后面的“Student”是新声明的派生类,冒号后面的“People”是已经存在的基类;在“People”之前有一关键宇 public,用来表示是公有继承;
继承方式包括 public(公有的)、private(私有的)和 protected(受保护的),此项是可选的,如果不写,那么默认为 private;
继承权限和继承方式
继承方式限定了基类成员在派生类中的访问权限,包括 public(公有的)、private(私有的)和 protected(受保护的);此项是可选项,如果不写,默认为 private(成员变量和成员函数默认也是 private);
现在我们知道,public、protected、private 三个关键字除了可以修饰类的成员,还可以指定继承方式;
public、protected、private 修饰类的成员
类成员的访问权限由高到低依次为 public –> protected –> private,public 成员可以通过对象来访问,private 成员不能通过对象访问;
现在再来补充一下 protected;protected 成员和 private 成员类似,也不能通过对象访问;但是当存在继承关系时,protected 和 private 就不一样了:基类中的 protected 成员可以在派生类中使用,而基类中的 private 成员不能在派生类中使用;
public、protected、private 指定继承方式
不同的继承方式会影响基类成员在派生类中的访问权限:
-
public继承方式
基类中所有 public 成员在派生类中为 public 属性;
基类中所有 protected 成员在派生类中为 protected 属性;
基类中所有 private 成员在派生类中不能使用。 -
protected继承方式
基类中的所有 public 成员在派生类中为 protected 属性;
基类中的所有 protected 成员在派生类中为 protected 属性;
基类中的所有 private 成员在派生类中不能使用。 -
private继承方式
基类中的所有 public 成员在派生类中均为 private 属性;
基类中的所有 protected 成员在派生类中均为 private 属性;
基类中的所有 private 成员在派生类中不能使用。
通过上面的分析可以发现:
-
基类成员在派生类中的访问权限不得高于继承方式中指定的权限
;例如,当继承方式为 protected 时,那么基类成员在派生类中的访问权限最高也为 protected,高于 protected 的会降级为 protected,但低于 protected 不会升级;再如,当继承方式为 public 时,那么基类成员在派生类中的访问权限将保持不变;
也就是说,继承方式中的 public、protected、private 是用来指明基类成员在派生类中的最高访问权限的; -
不管继承方式如何,基类中的 private 成员在派生类中始终不能使用(不能在派生类的成员函数中访问或调用)
; -
如果希望基类的成员能够被派生类继承并且毫无障碍地使用,那么这些成员只能声明为 public 或 protected;只有那些不希望在派生类中使用的成员才声明为 private;
-
如果希望基类的成员既不向外暴露(不能通过对象访问),还能在派生类中使用,那么只能声明为 protected;
注意,我们这里说的是基类的 private 成员不能在派生类中使用,并没有说基类的 private 成员不能被继承
;
实际上,基类的 private 成员是能够被继承的,并且(成员变量)会占用派生类对象的内存,它只是在派生类中不可见,导致无法使用罢了;
private 成员的这种特性,能够很好的对派生类隐藏基类的实现,以体现面向对象的封装性;
由于 private 和 protected 继承方式会改变基类成员在派生类中的访问权限,导致继承关系复杂,所以实际开发中我们一般使用 public;
在派生类中访问基类 private 成员的唯一方法就是借助基类的非 private 成员函数,如果基类没有非 private 成员函数,那么该成员在派生类中将无法访问;
改变访问权限
使用 using 关键字可以改变基类成员在派生类中的访问权限,例如将 public 改为 private、将 protected 改为 public 等:
#include <cstdio>
using namespace std;
class A {
protected:
int m_a;
char m_b;
float m_c;
private:
char *m_d;
};
class B : public A {
public:
using A::m_a;
protected:
using A::m_b;
// using A::m_d; // 基类的private成员在派生类中不可见
private:
using A::m_c;
};
int main() {
B b;
b.m_a = 1;
return 0;
}
注意:在派生类中只能访问基类的非 private 成员,使用 using 时,也必须遵循这个规则;
对于父类的 public/protected 成员:using 可以将其改为 public、protected、private 属性;
对于父类的 private 成员:由于在派生类中不可见,所以也就不能使用 using 改变属性;