小记:静默如初,安之若素
继承(Inheritance)
1. 继承的概念//了解
通过一种机制来表达类型之间共性和特征的方式,利用已有的数据类型定义新的数据类性,这种机制就是继承。
eg:
人类:姓名,年龄,吃饭,睡觉
学生类:姓名,年龄,吃饭,睡觉,学号,学习
教师类:姓名,年龄,吃饭,睡觉,工资,授课
----------------------------------
人类:姓名,年龄,吃饭,睡觉
学生类继承人类:学号,学习
教师类继承人类:工资,授课
----------------------------------
人类(基类)
/ \
学生类 教师类(派生类,子类)
注:基类派生子类, 子类继承基类
2. 继承的语法
class 子类:继承方式 基类,...
{
};
继承方式:
--》public:公有继承
--》protected:保护继承
--》private:私有继承
eg:
class Human{吃饭,年龄,睡觉,吃饭};
class Student:public Human
{
//学生继承了人类
//人类中的成员在当前学生类就可以直接适用
};
示例:
1 #include <iostream>
2 using namespace std;
3
4 //人类(基类)
5 class Human
6 {
7 public:
8 Human(const string& name, int age):m_name(name), m_age(age){}
9
10 void eat(const string & food)
11 {
12 cout << "Food : " << food << endl;
13 }
14
15 void sleep(int time)
16 {
17 cout << "Sleep time : " << time << endl;
18 }
19
20 protected://保护型成员可以在子类中被访问
21 string m_name;
22 int m_age;
23 };
24
25 //学生类(人类的一个子类)
26 class Student:public Human
27 {
28 public:
29 //说明从基类中继承过来的成员初始化方式
30 Student(const string &name, int age, int num)
31 :Human(name, age), m_num(num){}
32
33 void learn(const string& course)
34 {
35 cout << "learn lesson : " << course << endl;
36 }
37
38 void who(void)
39 {
40 cout << "my name : " << m_name << " , age : "<< m_age << " ,num : " << m_num << endl;
41 }
42 private:
43 int m_num;
44 };
45 //教师类(人类的另一个子类)
46 class Teacher:public Human
47 {
48 public:
49 Teacher(const string &name, int age, float salary)
50 :Human(name, age), m_salary(salary){}
51
52 void teach(const string &course)
53 {
54 cout <<"teach lesson : " << course << endl;
55 }
56 void who(void)
57 {
58 cout << "my name : " <<m_name << ", age : " << m_age << " ,salary : " << m_salary << endl;
59 }
60 private:
61 float m_salary;
62 };
63
64
65 int main(int argc, char *argv[])
66 {
67 Student s("red", 35, 10011);
68 s.who();
69 s.eat("noodle");
70 s.sleep(8);
71 s.learn("Chinese");
72
73 Teacher t("red2", 33, 8000.8);
74 t.who();
75 t.eat("rice");
76 t.sleep(7);
77 t.teach("English");
78
79 return 0;
80 }
3. 公有继承的特性(public)
1)子类对象会继承基类属性的行为,通过子类对象可以访问基类中的成员,如同是基类对象在访问它们一样。
注:子对象中包含基类的部分成为”基类子对象“:
2)向上造型:
将子类类型的指针或引用转换为基类类型的指针或引用,只能在公有继承中实现.
注:这种操作性缩小的类型的转换,在编译器看来是安全的可以直接隐式完成转换
基类
↑
子类
注:转的是指针和引用,不改变内存上的任何数据
eg:
class Base{...};//基类
class Derived:public Base {...};//子类
void func1(Base&){}
void func2(Base*){}
int main()
{
Devived d;
func1(d);//向上造型
func2(&d);//向上造型
}
3)向下造型
将基类类型的指针或引用转换为子类类型的指针或引用
注:这种操作性缩小的类型的转换,在编译器看来是危险的不可以直接隐式转换,但可以通过显示转换
基类
↓
子类
注:转的是指针和引用,不改变内存上的任何数据
4)子类继承基类的成员
–》在子类中,可以直接访问基类中的共有或保护成员,如同它们是子类自己的成员变量一般;
–》基类中的私有成员子类也可以继承过来,但是会收到访问控制属性的限制,无法直接访问;如果子类需要访问基类中的私有成员,可以通过基类提供的共有或保护的接口函数来间接访问。
–》基类的构造函数和析构函数,子类是无法继承的,但是可以在子类的构造函数使用初始化表显式指明基类部分的初始化方式。
1 #include <iostream>
2 using namespace std;
3
4 //人类(基类)
5 class Human
6 {
7 public:
8 Human(const string& name, int age):m_name(name), m_age(age),m_id(1234){}
9
10 void eat(const string & food)
11 {
12 cout << "Food : " << food << endl;
13 }
14
15 void sleep(int time)
16 {
17 cout << "Sleep time : " << time << endl;
18 }
19
20 protected://保护型成员可以在子类中被访问
21 string m_name;
22 int m_age;
23 //私有成员子类中无法直接访问,但是可以提供保护的借口函数来间接访问
24 const int& getId(void)
25 {
26 return m_id;
27 }
28 private:
29 int m_id;
30 };
31
32 //学生类(人类的一个子类)
33 class Student:public Human
34 {
35 public:
36 //Human(...) : 说明从基类中继承过来的成员初始化方式
37 Student(const string &name, int age, int num)
38 :Human(name, age), m_num(num){}
39
40 void learn(const string& course)
41 {
42 cout << "learn lesson : " << course << endl;
43 }
44
45 void who(void)
46 {
47 cout << "my name : " << m_name << " , age : "<< m_age << " ,num : " << m_num << endl;
48 cout << "ID num : " << getId() << endl;
49 }
50 private:
51 int m_num;
52 };
53 //教师类(人类的另一个子类)
54 class Teacher:public Human
55 {
56 public:
57 Teacher(const string &name, int age, float salary)
58 :Human(name, age), m_salary(salary){}
59
60 void teach(const string &course)
61 {
62 cout <<"teach lesson : " << course << endl;
63 }
64 void who(void)
65 {
66 cout << "my name : " <<m_name << ", age : " << m_age << " ,salary : " << m_salary << endl;
67 }
68 private:
69 float m_salary;
70 };
71
72
73 int main(int argc, char *argv[])
74 {
75 Student s("red", 35, 10011);
76 s.who();
77 s.eat("noodle");
78 s.sleep(8);
79 s.learn("Chinese");
80
81 Teacher t("red2", 33, 8000.8);
82 t.who();
83 t.eat("rice");
84 t.sleep(7);
85 t.teach("English");
86
87 //Student*--> Human*::向上造型
88 Human *ph = &s;
89 ph->eat("apple");
90 ph->sleep(16);
91
92
93 //Human*-->Student*::向下造型
94 Student *ps = static_cast<Student*>(ph);//不能隐式转换,但可以显示转换
95 ps->who();
96
97 Human h("red3", 22);
98 //Human*-->Student:向下造型
99 Student *ps2 = static_cast<Student*>(&h);
100 ps2->who();
101
102
103 return 0;
104 }
5)子类隐藏基类的成员
–》如果子类和基类中有同名的成员函数(变量),因为作用域不同,不会有重载关系,而是一种隐藏关系。如果需要访问基类中隐藏的成员函数,可以通过“基类::
”显式指明。
1 #include <iostream>
2 using namespace std;
3
4 class Base
5 {
6 public:
7 void foo(void)
8 {
9 cout << "Base::foo" << endl;
10 }
11 };
12
13
14 class Derived:public Base
15 {
16 public:
17 void foo(int i)
18 {
19 cout << "Derived::foo" << endl;
20 }
21 };
22
23 int main(int argc, char *argv[])
24 {
25 Derived d;
26 //加作用域限定符
27 d.Base::foo();
28 d.foo(10);
29
30 return 0;
31 }
4. 继承方式和访问控制属性
1)访问控制限定符:影响访问该成员的位置
访问控制限定符 访问控制属性 内部访问 子类访问 外部访问 友元访问
public 公有属性 ok ok ok ok
protected 保护成员 ok ok no ok
private 私有成员 ok no no ok
2)基类的继承方式:影响通过子类访问基类中的成员的可访问性
基类中的 在公有子类中变成 在保护子类中变成 在私有子类中变成
公有成员 公有成员 保护成员 私有成员
保护成员 保护成员 保护成员 私有成员
私有成员 私有成员 私有成员 私有成员
注:向上造型语法在保护继承和私有继承中不再适用
1 #include <iostream>
2 using namespace std;
3
4 class A
5 {
6 public:
7 int m_public;
8 protected:
9 int m_protected;
10 private:
11 int m_private;
12 };
13
14 class B:public A{};
15 class C:protected A{};
16 class D:private A{};
17
18 //子类的子类
19 //X公有继承B,B公有继承了A中的成员,
20 //等价于X保护继承了A的成员,从而导致了X能访问A中的除私有成员以外的所有成员
21 class X:public B
22 {
23 void func(void)
24 {
25 m_public = 10;
26 m_protected = 10;
27 //m_private = 10; error
28 }
29 };
30
31 //子类的子类
32 //Y公有继承C,C保护继承了A中的成员,
33 //等价于Y保护继承了A的成员,从而导致了Y能访问A中的除私有成员以外的所有成员
34 class Y:public C
35 {
36 void func(void)
37 {
38 m_public = 10;
39 m_protected = 10;
40 //m_private = 10; error
41 }
42 };
43
44 //子类的子类,Z公有继承D,D私有继承了A中的成员,
45 //等价于z私有继承了A的成员,从而导致了Z不能访问A中的所有成员
46 class Z:public D
47 {
48 void func(void)
49 {
50 //m_public = 10;
51 //m_protected = 10;
52 //m_private = 10;
53 }
54 };
56 int main(int argc, char *argv[])
57 {
58 B b;
59 b.m_public = 10;
60 //b.m_protected = 10; //error
61 //b.m_private = 10; //error
62
63 C c;
64 //c.m_public = 10; //error
65 //c.m_protected = 10; //error
66 //c.m_private = 10; //error
67
68 D d;
69 //d.m_public = 10; //error
70 //d.m_protected = 10; //error
71 //d.m_protected = 10; //error
72
73 return 0;
74 }
5. 子类的构造函数
1)如果子类构造函数没有指明基类部分(基类子对象)的初始化方式,那么编译器将会自动调用基类的无参构造函数来初始化。
2)如果希望基类子对象以有参的方式被初始化,需要在子类构造函数的初始化表中指明其初始化方式:
class 子类:public 基类
{
//基类(...):指明基类子对象的初始化方式
子类(...):基类(...){}
}
1 #include <iostream>
2 using namespace std;
3
4 class Base
5 {
6 public:
7 Base(void):m_i(0)
8 {
9 cout << "Base::Base(void)" << endl;
10 }
11 Base(int i):m_i(i)
12 {
13 cout << "Base::Base(int)" << endl;
14 }
15
16 int m_i;
17 };
18
19 class Derived:public Base
20 {
21 public:
22 Derived(void)
23 {
24 cout << "Derived::Derived(void)" << endl;
25 }
26 //Base(i):指明基类子对象初始化方式
27 Derived(int i):Base(i)
28 {
29 cout << "Derived::Derived(int)" << endl;
30 }
31 };
32
33 int main(void)
34 {
35
36 Derived d;
37 cout << d.m_i << endl;//0
38
39 Derived d2(123);
40 cout << d2.m_i << endl;//123
41 return 0;
42 }
3)子类对象的构造过程:
=》分配内存
=》构造基类子对象(按继承表顺序)
=》构造成员子对象(按声明的顺序)
=》执行子类的构造函数代码
1 #include <iostream>
2 using namespace std;
3
4 class Member
5 {
6 public:
7 Member(void):m_i(0)
8 {
9 cout << "Member::Member(void)" << endl;
10 }
11 Member(int i):m_i(i)
12 {
13 cout << "Member::Member(int)" << endl;
14 }
15 int m_i;
16 };
17
18 class Base
19 {
20 public:
21 Base(void):m_i(0)
22 {
23 cout << "Base::Base(void)" << endl;
24 }
25 Base(int i):m_i(i)
26 {
27 cout << "Base::Base(int)" << endl;
28 }
29
30 int m_i;
31 };
32
33 class Derived:public Base
34 {
35 public:
36 Derived(void)
37 {
38 cout << "Derived::Derived(void)" << endl;
39 }
40 //Base(i):指明基类子对象初始化方式
41 //m_m(i):指明成员子对象初始化方式
42 Derived(int i):Base(i),m_m(i)
43 {
44 cout << "Derived::Derived(int)" << endl;
45 }
46
47 Member m_m;//成员子对象
48 };
49
50 int main(void)
51 {
52
53 Derived d;
54 cout << d.m_i <<","<< d.m_m.m_i<< endl;//0
55
56 Derived d2(123);
57 cout << d2.m_i << "," << d2.m_m.m_i << endl;//123
58 return 0;
59 }
6. 子类的析构函数
1)子类的析构函数,无论是自己定义的还是编译器缺省提供的,都会自动调用基类的析构函数,析构基类子对象。
2)子类对象的析构过程:
--》执行子类析构函数代码
--》析构成员子对象(按声明的逆序)
--》析构基类子对象(按继承表的逆序)
--》释放内存
1 #include <iostream>
2 using namespace std;
3
4 class Member
5 {
6 public:
7 Member(void):m_i(0)
8 {
9 cout << "Member::Member(void)" << endl;
10 }
11 Member(int i):m_i(i)
12 {
13 cout << "Member::Member(int)" << endl;
14 }
15 ~Member(void)
16 {
17 cout << "Member::~Member(void)" << endl;
18 }
19 int m_i;
20 };
21
22 class Base
23 {
24 public:
25 Base(void):m_i(0)
26 {
27 cout << "Base::Base(void)" << endl;
28 }
29 Base(int i):m_i(i)
30 {
31 cout << "Base::Base(int)" << endl;
32 }
33 ~Base(void)
34 {
35 cout << "Base::~Base(void)" << endl;
36 }
37
38 int m_i;
39 };
40
41 class Derived:public Base
42 {
43 public:
44 Derived(void)
45 {
46 cout << "Derived::Derived(void)" << endl;
47 }
48 //Base(i):指明基类子对象初始化方式
49 //m_m(i):指明成员子对象初始化方式
50 Derived(int i):Base(i),m_m(i)
51 {
52 cout << "Derived::Derived(int)" << endl;
53 }
54 ~Derived(void)
55 {
56 cout << "Derived::~Derived(void)" << endl;
57 }
58
59 Member m_m;//成员子对象
60 };
61
62 int main(void)
63 {
64
65 Derived d;
66 cout << d.m_i <<","<< d.m_m.m_i<< endl;//0
67
68 //Derived d2(123);
69 //cout << d2.m_i << "," << d2.m_m.m_i << endl;//123
70 return 0;
71 }
3)基类的析构函数不会自动调用子类的析构函数。如果对一个指针子类对象的激烈指针适用delete操作符,实际被执行的仅是基类的析构函数,子类的析构函数执行不到,有内存泄露的风险。
class A{...};
class B:public A{...}
A* pa = new B;//pa:指向子类对象的基类指针
delete pa;//内存泄露
7. 子类的拷贝构造和拷贝赋值
1)拷贝构造函数
1.1)子类没有定义拷贝构造函数,编译器会为子类提供缺省的拷贝构造函数,该函数会自动调用基类的拷贝构造函数,以拷贝的方式来初始化基类子对象。
1.2)子类定义了拷贝构造函数,那么需要 使用初始化表显示指明基类子对象也要以拷贝方式来初始化:
class Base{...};
class Derived
{
public:
//Base(that):指明基类自队形以拷贝方式来初始化
Devied(const Derived& that):Base(that),...{}
};
2)拷贝赋值操作符函数
2.1)子类没有定义拷贝赋值函数,编译器会为子类提供缺省的拷贝赋值函数,该函数会自动调用基类的拷贝赋值函数,完成对基类子对象的复制。
2.2)子类定义了拷贝赋值函数,需要显式调用基类的拷贝赋值函数,完成对基类子对象的复制。
class Base{...};
class Derived
{
Derived& operator=(const Derived& that)
{
......
//显示调用基类的拷贝赋值函数
Base::operator=(that);
}
};
1 #include <iostream>
2 using namespace std;
3
4 class Base
5 {
6 public:
7 Base(void):m_i(0){}
8 Base(int i):m_i(i){}
9 Base(const Base& that):m_i(that.m_i)
10 {
11 cout << "Base::copy func" << endl;
12 }
13 Base& operator=(const Base& that)
14 {
15 cout << "Base::operat=" << endl;
16 if(&that != this)
17 {
18 m_i = that.m_i;
19 }
20 return *this;
21 }
22 int m_i;
23 };
24
25 class Derived:public Base
26 {
27 public:
28 Derived(void):m_i(0){}
29 Derived(int i1, int i2):Base(i1),m_i(i2){}
30
31 //Base(that):指明基类子对象以拷贝方式初始化
32 Derived(const Derived& that)
33 :m_i(that.m_i), Base(that){}
34
35 Derived& operator=(const Derived& that)
36 {
37 if(&that != this)
38 {
39 m_i = that.m_i;
40 //Base::m_i = that.Base::m_i;
41 //调用基类的拷贝赋值,复制基类子对象
42 Base::operator=(that);
43 }
44 return *this;
45 }
46
47 int m_i;
48 };
49
49
50
51 int main(void)
52 {
53 Derived d1(100, 200);
54 Derived d2(d1);//等价于Derived d2 = d1;
55 cout << d1.Base::m_i << " , " << d1.m_i << endl;
56 cout << d2.Base::m_i << " , " << d2.m_i << endl;
57
58 Derived d3;
59 d3 = d1;//d3.operator=(d1)
60 cout << d3.Base::m_i << " , " << d3.m_i << endl;
61
62 return 0;
63 }
8. 多重继承
1)一个子类同时继承多个基类,这样的继承结果成为多重继承。
2)多重继承在向上造型的转换时,编译器会很具各个基类子对象的内存布局,进行适当的偏移计算,保证指针的类型与其所指向的目标对象一致。
1 #include <iostream>
2 using namespace std;
3
4 class Phone
5 {
6 public:
7 Phone(const string& number):m_number(number){}
8
9 void call(const string& number)
10 {
11 cout << m_number << " call " << number << endl;
12 }
13 private:
14 string m_number;
15 };
16
17 class Player
18 {
19 public:
20 Player(const string& media):m_media(media){}
21
22 void play(const string &music)
23 {
24 cout << m_media << " play " << music << endl;
25 }
26 private:
27 string m_media;
28 };
29
30 class Computer
31 {
32 public:
33 Computer(const string &os):m_os(os){}
34
35 void run(const string &app)
36 {
37 cout << " on " << m_os << " run " << app << endl;
38 }
39
40 private:
41 string m_os;
42 };
43
44 class SmartPhone:public Phone, public Player, public Computer
45 {
46 public:
47 SmartPhone(const string &number, const string &media, const string &os)
48 :Phone(number), Player(media), Computer(os){}
49 };
50
51 int main(void)
52 {
53 SmartPhone iphoneX("1888001000", "MP4", "andriod");
54 iphoneX.call("010-001");
55 iphoneX.play("sun.mp3");
56 iphoneX.run("LOL");
57
58 SmartPhone *p1 = &iphoneX;
59 //向上造型
60 Phone *p2 = p1;
61 Player * p3 = p1;
62 Computer *p4 = p1;
63
64 cout << "sizeof(string) : " << sizeof(string) << endl;
65 cout << "p1 = " << p1 << endl;
66 cout << "p2 = " << p2 << endl;
67 cout << "p3 = " << p3 << endl;
68 cout << "p4 = " << p4 << endl;
69
70 return 0;
71 }
3)名字冲突问题
一个子类的多个基类如果存在相同的成员,当通过子类访问这些成员时,编译器会报歧义错误—名字冲突。(作用域限定方法解决)
解决名字冲突的一般做法就是显示的使用作用域限定操作符,指明所访问的成员属于哪个基类;
如果产生冲突的成员是成员函数并且满足不同参数的条件,则还可以通过using
声明,让它们在子类中形成重载关系,通过重载解析来解决。
eg:
class Base1
{
public:
int m_i;
};
class Base2
{
public:
int m_i;
};
class Derived:public Base1, public Base2
{
//子类中访问m_i; error
};
9. 钻石继承
1)定义:一个子类的多个基类源自共同的基类祖先,这样的继承结构成为钻石继承。
A(int m_i)
/ \
B C
\ /
D
1 #include <iostream>
2 using namespace std;
3
4 class A
5 {
6 public:
7 A(int data):m_data(data){}
8 protected:
9 int m_data;
10 };
11
12 class B: virtual public A //虚继承
13 {
14 public:
15 B(int data):A(data){}
16 void set(int data)
17 {
18 m_data = data;
19 }
20 };
21
22 class C: virtual public A //虚继承
23 {
24 public:
25 C(int data):A(data){}
26 int get(void)
27 {
28 return m_data;
29 }
30 };
31
32 class D: public B, public C
33 {
34 public:
35 //虚继承时,D类构造函数负责构造A基类子对象
36 D(int data):B(data),C(data),A(data){}
37 };
38
39 int main(void)
40 {
41 D d(100);
42 cout << d.get() << endl;//100
43 d.set(200);
44 cout << d.get() << endl;//100,error
45
46 return 0;
47 }
2)公共基类(A)子对象在汇聚子对象(D)对象中,存在多个 实例,在汇聚子类中或者通过汇聚子类对象,去访问公共基类的成员,会因为继承路径不同而导致结果不一致。
3)通过虚继承可以让公共基类(A)子对象在汇聚子类(D)对象中实例唯一,并为所有中间(B,C)子类对象共享,这样即使通过不同的继承路径所访问到公共基类的成员一定是一致的。
A (int m_i)(虚基类)
/ \(虚继承)
B C (virtual B, C)
\ /
D
10. 虚继承语法
1)继承表中使用virtual
关键字
2)位于继承链最末端子类负责构造公共基类子对象。