C++类的继承
类的继承----> 一个新类从一个已有的类那里获得其已有特性,称为累的继承(从一个已有的父类产生一个新的子类,称为类的派生)
单继承--->一个派生类只从一个基类派生,为单继承(简单说就是一个子类只有一个父类即为单继承)
多重继承--->一个子类有两个或多个父类就是多重继承。
继承声明形式:
class 派生类名: [继承方式] 父类名 {
派生类新加的成员
}
继承方式分为公有的(public)、私有的(private)、受保护的(protected),如果不写则默认为私有的(private)
受保护成员与私有成员除了受保护成员可以被派生类访问这一点是等价的。
构造一个派生类的三个步骤:
①从基类接收成员,出了构造函数和析构函数之外的所有成员都会被自动接收,无法控制。
②调整从基类接收的成员,具体的可以理解为对成员的覆盖或者重载等等。
③在声明派生类时增加的成员,即新增子类中独有的成员。
基类的成员 在公用派生类中的访问属性 在私有派生类中的访问属性 在保护派生类中的访问属性
私有成员 不可访问 不可访问 不可访问
公有成员 公有 私有 受保护
受保护成员
受保护
私有
受保护
派生类声明构造函数以及调用基类的构造函数形式:
派生类构造函数名(总参数列表):基类构造函数名(参数列表) {
函数体
}
如:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 |
//基类#include <iostream>#include <string>using
namespace std;class
Student { public: Student() {}; Student(int
a,string b,char
c) { this->num = a; this->name = b; this->sex = c; }; string name; void
display() { cout<<"number: "<<num<<endl; cout<<"name: "<<name<<endl; cout<<"sex: "<<sex<<endl; }; private: int
num; char
sex;}; |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
//派生类#include "Student.h"class
msStudent: public
Student { public: msStudent(int
a, string b, char
c, int
e, string f, char
g, string d): Student(a,b,c), monitor(e,f,g) { //<span style="background-color: rgb(255, 204, 0);">定义构造函数并调用Student基类的构造函数,也可以放到类外定义,声明时无需冒号之后的部分,但在定义是要写全,声明:msStudent(int a, string b, char c, string d);定义时写为msStudent::msStudent(int a, string b, char c, string d):Student(a,b,c){}即可其中monitor为子对象</span> this->sName = d; }; void
msdisplay () { cout<<"Middle Schoole: "<<sName<<endl; cout<<monitor.name<<endl; }; private: string sName; Student monitor;//<span style="background-color: rgb(0, 255, 0);">monitor为子对象即类内对象</span>}; |
执行顺序为:先执行基类的构造函数,在执行子对象构造函数数据初始化,然后执行派生类的构造函数。同时派生类对象释放时先执行派生类的构造函数,然后执行基类的构造函数。
多重继承:
当多重继承的多个基类中有同名的成员时会有歧义,此时我们需要引入基类名来限定,如:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
class
A { public: int
a; }class
B { public: int
a;}class
C : public
A,public
B { public: int
c;}C c1;c1.A::a = 2;//使用基类名来限定可防止同名成员的混淆c1.B::a = 3; |
当派生类中的成员与基类中的成员同名时会将基类中的成员覆盖掉!而如果想要访问基类中的成员可使用基类名来访问,如:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
class
A { public: int
a; }class
B { public: int
a;}class
C : public
A,public
B { public: int
a;}C c1;c1.A::a = 2;//使用基类名来限定可防止同名成员的混淆c1.B::a = 3;//使用基类名来限定可防止同名成员的混淆c1.a = 4;//这几个a互不影响,可正常运行 |
虚基类:使继承间接共同基类时只保留一份成员。
声明虚基类的形式:class 派生类名: virtual 继承方式 基类名,如:class B: virtual public A {};
虚基类的初始化: 虚基类的初始化与常规的不同,由于虚基类只保留一份成员,为了避免虚基类的直接派生类在初始化时对基类的初始化不同,所以虚基类的数据成员初始化的值必须由最后的派生类直接给出,如:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
class
A { A(int
i){ };};class
B: virtual
public A { B(int
n): A(n){ };};class
C: virtual
public A { C(int
n): A(n){ };};Class D: public
B,public
C { D(int
n): A(n),B(n),C(n) {//<span style="background-color: rgb(255, 204, 0);">最后的派生类直接赋值,A中成员的赋值只进行一次,不会因为B和C而进行三次,因为C++编译系统只执行最后的派生类对虚基类的构造函数的调用</span> }}; |
可以直接用派生类的对象赋值给基类的对象,派生类对象特有的成员将会被自动舍弃!他们是赋值兼容的,就像int数据可以赋值给double数据一样。注意,基类的对象的引用或者指针都可以用派生类对象直接赋值。如:
|
1
2
3
4
5
6
7
8
9 |
class
A {};class
B: public
A {};A a;B b;A &r1 = a;//合法的A &r2 = b;//合法的,<span style="background-color: rgb(255, 204, 0);">b中独有的成员会被自动舍弃,而其他基类中存在的数据他们与派生类中的成员会共享同一个存储单元<br><span style="background-color: rgb(255, 255, 255);">A *p = &b;//也是合法的</span></span> |
如果派生类中有与基类同名的成员,引用和指针指向的仍然是从基类继承来的成员,如class A {public:
|
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 |
<em id="__mceDel"><em id="__mceDel"> A(int
a = 3) { this->a = a; }; int
a;};class
B: public
A {public: B(int
a = 5) { this->a = a; }; int
a;};int
main() { A a; B b; A *p = &a; cout<<p->a; // 3 p = &b; cout<<p->a; //<span style="background-color: rgb(255, 255, 0);">仍然是3</span> cout<<b.a; //5 return
0;};</em></em> |