c++类——继承(一)

本文最后更新于:1 年前

(一)概念:

继承机制是面向对象程序设计中最重要的一个概念,也是使代码可以复用的最重要的手段。

继承机制允许我们依据一个类来定义另一个类,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,能使对类的创建和维护更加容易——能够重用代码功能和提高执行效率。

被继承的类,也就是已有的类,被称为基类(父类),而发生继承的类,也就是新建的类,被称为派生类(子类)。

如果要使用基类的成员,派生类不需要重新编写新的数据成员和成员函数,只需要指定继承的已有的类的成员。但是,派生类不能继承基类的构造函数、析构函数、拷贝构造函数、重载运算符和友元函数。

一个基类可以派生出多个派生类,一个派生类也可以从多个基类中继承数据成员和成员函数。

派生类只能访问基类的非私有成员。

(二)继承的语法形式:

class 派生类名:基类名表

{

数据成员和成员函数声明

};

其中,基类和一般情况下声明的类基本相同。

基类名表的组成为:

访问控制 基类名1 , 访问控制 基类名2 , .......

访问控制是关键字,表示派生类对基类的继承方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
using namespace std;
class person
{
};
class animal
{
};
class knowledge{};
class student :public animal,protected person,private knowledge
{
};
class teacher:public animal, protected person, private knowledge
{
};

(三)继承的三种方式:

访问控制的关键字共有三个,分别为public、private和protected。

如果未直接指定访问控制关键字,默认为private。

访问控制决定了不同的访问权限和访问类型,详细情况见下表。

可以看到,对于基类成员,派生类的继承如下:

- 基类的私有成员,派生类不可以访问

- 基类的保护成员,派生类可以继承为自己的保护成员(protected继承)和私有成员(private继承),在派生类可以访问,在外部不可以访问。

- 基类的公有成员,子类可以继承为自己的公有成员(public继承),保护成员(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
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
#include<iostream>
using namespace std;
class Axy //基类
{
private:
string Aname;
protected:
double x;
double y;
public:
Axy(double d1 = 0, double d2 = 0,string s = "pointA" ):Aname(s),x(d1),y(d2){ }
double getx() { return x; }
double gety() { return y; }
string getAname() { return Aname; }
};
class Bxy :public Axy //公有继承A,除了不能继承Aname,其余保持不变,构造函数不继承需要重新写
{
private:
string Bname;
public:
//对基类的一种初始化方式
Bxy(double d1 = 0, double d2 = 0, string s1 = "pointB",string s2 ="pointA") :Bname(s1), Axy(d1, d2, s2) {}
};
class Cxy:protected Axy //保护继承A,不能继承Aname,其他均变为protected类型,构造函数不继承需要重新写
{
private:
string Cname;
public:
Cxy(double d1 = 0, double d2 = 0, string s1 = "pointC") :Cname(s1)
{
//继承可以直接使用基类的非私有数据成员
x = d1;
y = d2;
}
void use_A_function()
{
//继承可以使用基类的非私有成员函数
cout << getx() << endl;
cout << gety() << endl;
return;
}
void use_A_function2()
{
cout << x << endl;
cout << y << endl;
return;
}
};
class Dxy:private Cxy //私有继承C,不能继承Cname,其他均变为private类型,构造函数不继承需要重新写
{
private:
string Dname;
public:
Dxy(double d1 = 0, double d2 = 0, string s = "pointD") :Dname(s), Cxy(d1, d2) {}
};
class Exy :public Dxy //保护继承D,因为上面均变为private类型,所以什么都都没继承,只能对其构造和析构
{
private:
string Dname;
public:
Exy(double d1 = 0, double d2 = 0, string s = "pointD") :Dname(s), Dxy(d1, d2) {}
};
int main()
{
Bxy b(1,1,"bbb","aaa");
cout << "b:" << b.getx() << "," << b.gety() << endl; //调用A的成员函数处理B的数据成员
cout << "Aname:" << b.getAname() << endl;
Cxy c; //c为保护继承,类外均不能调用原有的成员函数
c.use_A_function(); //通过调用C的成员函数调用A的成员函数处理C的数据成员
c.use_A_function2(); //调用C的成员函数对C的数据成员操作
Dxy d; //d为私有继承,类外均不能调用原有的成员函数
Exy e; //e为公有继承,但什么都没继承到,所以没有可以调用的成员函数。
cout << sizeof(Axy) << endl;
cout << sizeof(Bxy) << endl;
cout << sizeof(Cxy) << endl;
cout << sizeof(Dxy) << endl;
cout << sizeof(Exy) << endl;
return 0;
}
//运行结果:
//b:1, 1
//Aname : aaa
//0
//0
//0
//0
//56
//96
//96
//136
//176

(四)重名成员:

  • 同名也称为隐藏,派生类定义了与基类同名的成员时,派生类的同名成员会屏蔽掉基类的同名成员——子类优先。

  • 如果要使用基类的同名成员,需要显式地使用类名限定符。

  • 对于成员函数,同名的要求是函数名相同,对参数列表没有要求。

  • 注意基类和派生类的作用域是独立的(表现为各有自己的this指针),但是基类的作用域被延伸到了派生类中(也就是在派生类的作用域中可以调用基类的作用域)。

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
#include<iostream>
using namespace std;
class Axy
{
private:
string Aname;
protected:
double x;
double y;
public:
Axy(double d1 = 0, double d2 = 0,string s = "pointA" ):Aname(s),x(d1),y(d2){ }
double getx() { return x; }
double gety() { return y; }
string getAname() { return Aname; }
};
class Bxy :public Axy
{
private:
string Bname;
protected:
double x;
double y;
public:
Bxy(double d1 = 0, double d2 = 0, double d3=0,double d4=0,string s1 = "pointB",string s2 ="pointA"):x(d1),y(d2),Bname(s1), Axy(d3, d4, s2) {}
double getx() { return x; } //访问默认的子类的同名变量x
double gety() { return y; } //访问默认的子类的同名变量y
double getAx() { return Axy::x; } //访问基类的同名变量x
double getAy() { return Axy::y; } //访问基类的同名变量y
string getAname() { return Axy::getAname(); } //调用基类的成员函数getAname,这里绝对不能不加作用域,否则会陷入死循环。
string getBname() { return Bname; }
};

int main()
{
Bxy b;
cout << "b:" << b.getx() << "," << b.gety() << endl;
cout << "a:" << b.getAx() << "," << b.getAy() << endl;
cout << "a:" << b.Axy::getx() << "," << b.Axy::gety() << endl;
cout << "bname:" << b.getBname() << endl;
cout << "aname:" << b.getAname() << endl;
cout << "aname:" << b.Axy::getAname() << endl;
return 0;
}
//运行结果:
//b:0, 0
//a : 0, 0
//a : 0, 0
//bname : pointB
//aname : pointA
//aname : pointA

(五)派生类访问静态成员:

  • 基类定义的静态成员将被所有派生类共享。

  • 根据静态成员自身的访问特性和派生类的继承方式,类层次体系中有不同的访问性质。

  • 派生类中访问静态成员,用以下形式显式说明:

    • 类名::成员
    • 对象名.成员
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
#include<iostream>
using namespace std;
class A
{
public:
static int i;
static void add_i()
{
i++;
return;
}
void print() { cout << "i:" << i << endl; }
};
int A::i = 0;
class B:protected A
{
public:
void add_i2()
{
i++;
return;
}
};

int main()
{
A a;
B b;
a.i++;
a.add_i();
b.add_i2();
//b.A::add_i(); protect继承不能在类外访问A的成员
//B::A::add_i(); 这样也不行
A::i++;
A::add_i();
a.print();
return 0;
}
//运行结果:
//i:5

c++类——继承(一)
https://github.com/xiaohei07/xiaohei07.github.io/2023/03/31/c++类——继承(一)/
作者
07xiaohei
发布于
2023年3月31日
许可协议