forked from segwegwg/Some-useful-code
-
Notifications
You must be signed in to change notification settings - Fork 0
/
No5继承.txt
207 lines (163 loc) · 8.78 KB
/
No5继承.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
继承机制:派生类含有基类的所有成员的同时还有一些拓展的成员
派生类的声明方式:
class 派生类名:类派生表(继承方式1 基类1,继承方式2 基类2,继承方式3 基类3)
class Person
{
};
class Student:public Person/private Person/protect Person
{
};
如果是多继承:class A: public B,public C,private D,private E
{
};
派生类对象的构成:包含基类的匿名对象(无名对象)
class Student{
private:
string s_id;
string s_name;
public:
Student(string id,string name)
{
s_id=id;
s_name=name;
}
void display()
{
cout<<s_id<<" "<<s_name<<endl;
}
};
class Collegestudent:public Student
{
private:
string m_major;
public:
void displaymajor()
{
cout<<m_major<<endl;
}
};
分析:派生对象collegeStudent中除了自己的m_major成员外,还有一个基类的Student类对象,不过这个对象是匿名存在的,Student指的是这个对象的类型。
即派生类对象创建的时候不但要给自己的数据成员创建存储空间,还要给自己继承的基类的数据成员创建存储空间。
与组合的差别是,组合的类内类成员的名字是知道的,而继承的基类名字未知。
派生类成员的访问:
1.派生类成员函数访问自己的数据成员、派生类对象访问派生类的数据成员(常规)
2.派生类的成员函数访问基类的成员 派生类的对象访问基类的数据成员(关键问题的关键)
private成员只有自己的类内成员函数可以访问,所以派生类成员函数无法访问基类的private数据成员。
protected修饰符:
protected访问权限仅提供了一种介于public和private之间的访问权限;比private多了可以被子类的成员函数访问,比public少了不能直接访问
private:只能由1.该类中的函数、2.其友元函数访问。不能被任何其他访问,该类的对象也不能访问。
protected:可以被1.该类中的函数、2.子类的函数、以及3.其友元函数访问。但不能被该类的对象访问。
public:可以被1.该类中的函数、2.子类的函数、3.其友元函数访问,也可以由4.该类的对象访问。
注:友元函数包括3种:设为友元的普通的非成员函数;设为友元的其他类的成员函数;设为友元类中的所有成员函数。
public继承:基类在派生类中的访问权限不变;
protected继承:(向封装方向兼容)基类中原来的public、protecetd在派生类中的访问权限变为protected,private不变;
private继承:基类的访问权限在派生类中变为private;
规则:比较继承方式和基类成员的访问权限,取优先级(公有>保护>私有)最低的作为在派生类中的访问权限;
多继承问题 A->B->C b为a的派生类,c为b的派生类;b为c的直接基类,a为b的直接基类,为c的间接基类。一个派生类可能有多个基类
在前面给出的单继承基础上,首先将B和他的基类调正规则,调整基类成员,让后将其看作一个大类AB,之后再将C看作是继承自类AB以此类推。
继承下的成员访问规则:(1)定义类对象后,通过类对象只能访问public成员
(2)在派生类成员函数中,能直接访问基类的public,protect成员,而不能访问private成员。
(3)派生类定义对象后再通过派生类对象访问基类的成员,可根据继承方式调整基类成员的访问权限后,将派生类和基类看作一个整体使用规则1.
public继承才是“is a”关系,而protected与private继承不是。
基类与派生类的关系:
1.替换规则:仅针对public继承:派生类对象也是基类对象,基类对象不是派生类对象。
class Person()
{
};
class Student:public Person{
};
void walk(Person P)
{
}
void study(Student S)
{
}
int main()
{
Person p;
Student s;
walk(p);
walk(s);//子类对象可以作为实参传入以基类作为形参的函数;
study(s);
study(p)//错误,基类对象不是一个派生类;
}
基类与派生类的转换:有没有强制类型转换?
派生类是基类的子类型;
首先,派生类中含有一个基类的匿名对象,所以派生类对象可以强制类型转换为基类对象;
Person p;
Student s;
p=s;//派生类对象转换为基类;
但是派生类对象含有基类没有的成员,所以基类无法强制转换为派生类;
基类的引用变量可以绑定派生类对象(本质是绑定了派生类对象中的匿名基类对象),而派生类的引用无法绑定基类对象。(is a 逻辑)
基类指针变量可以指向派生类对象地址,派生类指针无法指向基类地址
但即使是通过基类的引用、指针可以访问派生类成员,也只能访问派生类继承来的成员,因为基类的指针引用,本质上是与派生类的匿名基类成员绑定的;
内在逻辑,派生类的指针和引用是指向派生类的,其必须要能够调用派生类的所有公有成员函数,如果指向绑定的是基类,那么逻辑错误。
派生类对基类的同名成员隐藏
作用域外和作用域内同名的变量、函数都会被隐藏。派生类会隐藏基类中与派生类同名的成员函数与数据成员。如果需要访问基类成员,需要加上 "基类::"
class Father
{
protected:
string sex;
int age;
};
class Son:public Father
{
public:
int age;
void f();
}
void f()
{
age=10;
Father::age=30;
}
继承的构造函数与析构函数:
(1)派生类的构造函数需要调用基类的构造函数,而且必须在函数参数初始化列表中调用。
(2)如果基类有默认构造函数,可以不在参数初始化列表中显示调用。
(3)基类构造函数将先于派生类构造函数调用。
(4)派生类析构函数会自动调用基类的析构函数,派生类析构先于基类析构调用(后进先出的栈结构)
易错:派生类的构造函数必须在参数初始化列表中调用基类的构造函数,不能在派生类函数内部调用!!!
简单派生构造函数:
class Person
{
protected:
string m_name;
public:
Person(const string&name): m_name(name)
{
cout<<"construct of Person"<<endl;
}
}
class Student:public Person
{
public:
Student(const string&name,int sco):Person(name),m_score(sco)//派生类构造函数以函数初始化列表的形式构造;
{
cout<<"construct of Student"<<endl;
}
private:
int m_score;
}
int main()
{
Student s("liming",100);
return 0;
}
派生类有其他类的对象作为数据成员时:
派生类构造函数名(形参列表):基类构造函数名(参数表),子对象名(参数表)
{
派生类其他数据成员的初始化语句;
}
构造函数调用顺序:基类构造函数、其他类构造函数、派生类构造函数;
多级继承的派生类构造函数
通常情况下,派生类构造函数只能调用其直接基类的构造函数,不要调用间接基类的构造函数。(特殊情况:虚基类)
派生类构造函数只需要调用其直接基类的构造函数,而其直接基类又负责它自己的直接基类的构造函数...
这种递推关系导致创建最底层派生类对象时,将从最顶层的基类开始依次向下调用基类构造函数,直到其直接基类的构造函数,最后调用派生类自己的构造函数。
当某些基类中包含子对象(对象作为数据成员)时,将在同级基类构造函数调用后调用子对象的类构造函数,以此类推。
派生类的析构函数的调用规则恰好与构造函数的调用顺序相反:派生类析构函数->其他类析构函数->基类析构函数
派生类调用直接基类的析构函数对其中的匿名对象实现析构,同时基类的析构函数调用自己的直接基类的析构函数,若派生类含有其它类的子对象,需要在同级的派生类析构函数调用结束后调用类析构函数,再调用它的基类的析构函数。
需要显示调用派生类的情况:
(1)派生类的新增数据成员需要初始化;
(2)派生类的直接基类没有定义默认构造函数。
(3)派生类包含其他类对象,而这些类没有定义默认构造函数。
(4)派生类的间接基类中包含的虚基类没有默认构造函数。