继承是面向对象复用的重要手段。通过继承定义一个类,继承是类型之间的关系建模,共享公有的东西,实现各自本质不同的东西。简单的说,继承就是指一个对象直接使用另一对象的属性和方法。C++中的继承关系就好比现实生活中的父子关系,继承一套房子通常比白手起家自己挣要容易得多。所以原始类被称为父类或基类,继承类称为子类或派生类,而子类又可以当成父类,可再被其它类继承。这种关系和java是一样道理,不过C++多了一个麻烦的地方就是它还支持多继承,于是就引发出很多坑人的地方。
继承的方式:
(1) 公有继承(public)
基类的公有和保护成员被子类继承时,它们都保持原有的状态,而基类的私有成员同样被继承下来,只是在子类表现为私有,子类不能访问。
(2)私有继承(private)
它的特点是基类的公有和保护成员被子类继承时,都会成为子类的私有成员;基类的私有成员也被继承下来,但不能被该子类访问。
(3)保护继承(protected)
它的特点是基类的公有成员和保护成员被子类继承时,都会成为子类的保护成员,子类的子类可通过保护成员函数或友元访问;基类的私有成员被继承下来仍然是私有的,依旧不能被子类访问。
private能够对外部和子类保密,即除了成员所在的类本身可以访问之外,别的都不能直接访问。protected能够对外部保密,但允许子类直接访问这些成员。不难看出protected限定符是因为继承才能表现出作用。
各种继承方式下各种成员关系变化如下图
继承方式就像一张‘网’,被继承后都成了跟‘网’权限“相同的”和比“网” ’“小”的。
总结一下:
1. public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每个子类对象也都是一个父类对象。
2. protetced/private继承是一个实现继承,基类的部分成员并未完全成为子类接口的一部分,是 has-a 的关系原则,所以非特殊情况下不会使用这两种继承关系,在绝大多数的场景下使用的都是公有继承。
3. 不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,但是基类的私有成员存在但是在子类中不可见(不能
访问)。
4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
赋值兼容规则
要点:
1. 子类对象可以赋值给父类对象(支持对象切片)
2. 父类对象不能赋值给子类对象
3. 父类的指针/引用可以指向子类对象
4. 子类的指针/引用不能指向父类对象(可通过强制类型转换完成)
例子:
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 class Person
6 {
7 public:
8
void Show()
9 {
10
cout<<_name<<endl;
11 }
12 protected:
13
string _name;
14 };
15
16 struct Student : public Person
17 {
18 public:
19
void Print()
20 {
21
cout<<_name<<endl;
22 }
23
24 //private:
25 public:
26
string _id;
27 };
28
29 void test1()
30 {
31 Person p;
32 Student s;
33
34
p = s; // 不是隐式类型转换 -- 切片处理-编译器天然支持 -- is-a
35
Person* pP = &s; //通过
36
Person& p1 = s; //通过
37
//上面都属于一种向上类型的转换,编译器默认支持。
38
//s = p; //报错,类型不匹配
39
Student* p3 = (Student*)&p; //编译通过,通过p3进行操作会出错
40
//p3->_id = 10; //该句执行完虽给p3->_id赋了值,但在整个程序结束时程序会崩掉,因为强转为了子类的指针,那么编译器就会按子类
41
//的大小对该指针作解释,这样就多‘占用’一块内存,而它可能是用来执行其他的活动的,但p3->id却指向这块非法的内存。
42
Student& r3 = (Student&)p; //编译通过,用r3进行操作出错
43
//Student& r3 = p; //出错
44
//r3._id = 10; //运行出错,同上面道理
45
46 }
注:p = s操作时会将子类对象独有的(非继承的部分)函数和变量自动“切去”,子类只留下继承来的基类原有的“切片”来对基类的对象进行赋值。