继承的权限
类中的任何成员都具有访问权限,派生类中的继承成员也不例外。派生类中继承成员的访问属性由定义派生类时的继承方式来决定,并不是简单地把基类的私有成员或公有成员直接作为派生类中的私有成员或公有成员使用。
与类中成员的访问限定符类似,继承方式也有public、protected和private三种,不同的继承方式,会使基类中访问属性不同的成员在派生类中的访问权限有所区别。访问方式的不同主要体现在以下两个方面:
● 派生类新增成员对从基类继承来的成员的访问方式。
● 派生类对象对从基类继承来的成员的访问方式。
下面分别对这三种继承方式进行详细讲解。
1、公有继承—public方式
公有继承是指通过public方式继承基类,公有继承的派生类定义形式如下所示:
class 派生类名称:public 基类名称
{
派生类成员声明
};
上一节案例中派生类Cat就是对基类的公有继承。
在讨论公有继承方式对派生类访问基类成员产生的影响前,可将基类成员按操作权限分为四类,分别为public成员、protected成员、private成员和不可访问成员。其中,protected成员主要出现在基类中,只为派生类开放权限。若类中成员声明具有protected访问权限,则该成员只可在本类和派生类中访问,其访问权限由派生类的继承方式决定。
所谓不可访问成员是指无论在类内还是在类外均不可访问的成员,它与私有成员的区别是,私有成员在类外不可访问,但是在类内可以访问。不可访问成员完全是由类的派生形成的。对于顶层类,不存在不可访问成员,但通过继承,原基类的私有成员在派生类内就成为不可访问成员。
以公有方式继承后,基类中四种不同属性成员在派生类中的操作权限如表1所示。
表1 公有继承对基类成员的访问属性控制
公有继承 | 基类成员 | |||
---|---|---|---|---|
public成员 | protected成员 | private成员 | 不可访问成员 | |
作为派生类成员 | public成员 | protected成员 | 不可访问成员 | 不可访问成员 |
从表1看出公有继承后,基类的public、protected成员在派生类中仍具有相同访问权限,派生类中新增成员可以访问它们,派生类对象可以访问public成员。private成员和不可访问成员,在派生类中成为不可访问成员,即在派生类内部也不可访问。
接下来通过一个案例说明公有继承时派生类中成员的操作方法,如例1所示。
例1
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 class Animal //定义基类Animal
6 {
7 public:
8 //设置m_nWeightBase属性的成员函数
9 void set_weight(int weight){ m_nWeightBase = weight; }
10 //获取m_nWeightBase 值的成员函数
11 int get_weight(){ return m_nWeightBase; }
12 //设置m_nAgeBase属性的成员函数
13 void set_age(int age){ m_nAgeBase = age; }
14 protected:
15 int m_nAgeBase; //定义保护成员m_nAgeBase
16 private:
17 int m_nWeightBase; //定义私有成员m_nWeightBase
18 };
19 class Cat:public Animal //通过公有继承,定义派生类Cat
20 {
21 public:
22 Cat(string con_name):m_strName(con_name){}
23 //定义函数print_age(),该函数访问基类成员m_nAgeBase
24 void print_age(){ cout << m_strName << ", age = " << m_nAgeBase << endl; }
25 private:
26 string m_strName; //派生类的私有成员
27 };
28 int main()
29 {
30 Cat cat("Persian"); //定义派生类对象cat
31 cat.set_age(5); //派生类对象调用从基类继承的公有成员函数
32 cat.set_weight(6); //派生类对象调用从基类继承的公有成员函数
33 cat.print_age(); //派生类对象调用自己的公有函数
34 cout << "cat weight = " << cat.get_weight() << endl;
35 system("pause");
36 return 0;
37 }
程序运行结果如图1所示。
图1 例1运行结果
在例1中,Cat类是基类Animal的公有派生类。继承后,基类中的public、protected成员在派生类中属性相同,从运行结果看,代码第31、32、34行中派生类对象正确调用了从基类继承的公有成员函数set_age()、set_weight()、get_weight()。对于保护成员m_nAgeBase,在派生类中可以直接访问,因此第24行的print_age()函数也能正确执行。
若在Cat中增加新成员函数print_weight(),访问基类的私有成员m_nWeightBase,代码如下:
void print_weight() { cout << "weight = " << m_nWeightBase << endl; }
编译出错,如图2所示。
图2 派生类中操作基类私有成员错误信息
根据公有继承的特点,可以描绘出派生类Cat与基类的关系以及Cat中各成员的访问属性,如图3所示。
图3 派生类Cat与基类Animal的继承关系
2、私有继承-private方式
私有继承是指通过private方式继承基类,私有继承的派生类定义形式如下:
class 派生类名称:private 基类名称
{
派生类成员声明
};
私有继承也会对基类中四种不同属性成员的访问方式产生影响。私有继承后,基类的public、protected成员在派生类中被限定为具有private访问权限,基类的private和不可访问成员被限定为不可访问。私有继承对派生类中继承成员的访问限定如表2所示。
表2 私有继承对基类成员的访问属性控制
私有继承 | 基类成员 | |||
---|---|---|---|---|
public成员 | protected成员 | private成员 | 不可访问成员 | |
作为派生类成员 | private成员 | private成员 | 不可访问成员 | 不可访问成员 |
接下来将例1修改为Cat类私有继承自Animal类,来演示私有继承后派生类成员的操作方法,如例2所示。
例2
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 class Animal //定义基类Animal
6 {
7 public:
8 //设置m_nWeightBase属性的成员函数
9 void set_weight(int weight){ m_nWeightBase = weight; }
10 //获取m_nWeightBase值的成员函数
11 int get_weight(){ return m_nWeightBase; }
12 //设置m_nAgeBase属性的成员函数
13 void set_age(int age){ m_nAgeBase = age; }
14 protected:
15 int m_nAgeBase; //定义保护成员m_nAgeBase
16 private:
17 int m_nWeightBase; //定义私有成员m_nWeightBase
18 };
19 class Cat:private Animal //通过私有继承,定义派生类Cat
20 {
21 public:
22 Cat(string con_name):m_strName(con_name){}
23 //定义函数set_print_age(),该函数访问基类公有成员函数set_age()和保护成员m_nAgeBase
24 void set_print_age()
25 {
26 set_age(5); //调用基类的成员函数
27 cout << m_strName << " age = " << m_nAgeBase << endl;
28 }
29 //定义函数set_print_weight(),该函数访问基类公有成员函数set_weight()、get_weight()
30 void set_print_weight()
31 {
32 set_weight(6); //调用基类的成员函数
33 cout << m_strName << ", weight = " << get_weight() << endl;
34 }
35 private:
36 string m_strName; //派生类的私有成员
37 };
38 int main()
39 {
40 Cat cat("Persian"); //定义派生类对象cat
41 cat.set_print_age();
42 cat.set_print_weight(); //派生类对象调用自己的公有函数
43 system("pause");
44 return 0;
45 }
程序运行结果如图4所示。
图4 例2运行结果
在例2中,类Cat私有继承自Animal类,继承后基类的public、protected成员在派生类中成为private成员,故基类的成员函数和m_nAgeBase数据成员只能在Cat类内使用。在Cat中,代码第24-28行定义了set_print_age()函数,该函数调用了基类的set_age()函数,访问了m_nAgeBase成员。类似的,第30-34行定义了set_print_weight()函数,调用了基类的set_weight()、get_weight()函数。main()函数中定义Cat对象通过调用公有函数set_print_age()、set_print_weight()显示了正确结果。
若在main()函数中,想通过cat对象直接操作从基类私有继承来的set_age()函数,语句如下:
cat.set_age(5);
出现编译错误,提示如图5所示。
图5 派生类对象操作私有继承的基类成员函数
根据私有继承的特点,可以描绘出例3中派生类Cat与基类的关系以及Cat中各成员的访问属性,如图6所示。
图6 私有继承方式下派生类各成员属性
由私有继承的特点可知,私有继承后,所有基类成员均成为派生类中的私有成员或不可访问成员,若进一步派生,则基类中的成员在派生类中都将成为不可访问成员,实际相当于终止了基类功能在子类中的派生。
3、保护继承-protected方式
保护继承是指通过protected方式继承基类,保护继承的派生类定义形式如下所示:
class 派生类名称:protected 基类名称
{
派生类成员声明
};
若派生类保护继承自基类,基类的public、protected成员在派生类中被限定为具有protected访问权限,基类的private和不可访问成员被限定为不可访问。保护继承对继承后的成员的访问限定如表3所示。
表3 保护继承对基类成员的访问属性控制
保护继承 | 基类成员 | |||
---|---|---|---|---|
public成员 | protected成员 | private成员 | 不可访问成员 | |
作为派生类成员 | protected成员 | protected成员 | 不可访问成员 | 不可访问成员 |
对于保护继承和私有继承来说,从基类继承来的public、protected成员在派生类内都可以访问,但对于保护继承来说,若把派生类作为基类再派生新类时,新派生的类中可以访问它直接基类中的protected成员,对于私有继承来说,派生类若再派生新类,则新类不可访问直接基类中的成员。如有下面的继承关系,则保护继承和私有继承会有不同,具体内容如下所示:
//保护继承
class A
{
public:
int n; //类中的公有成员
};
//保护继承后,从类A中继承的成员n具有protected属性,类内可以访问
class B:protected A{};
//保护继承后,从B类继承的成员n具有protected属性,类内可访问
class C:protected B{};
//私有继承
class A
{
public:
int n; //类中的公有成员
};
//私有继承后,从类A中继承的成员n具有private属性,类内可以访问
class B:private A{};
//私有继承后,从B类继承的成员n成为不可访问成员,类内不可访问
class C:private B{};
接下来对例1进行修改,例中将定义三个类,Cat类保护继承自Animal类,PersianCat类保护继承自Cat类,它们构成了继承的三层结构,通过对PersianCat对象的操作来学习保护继承后派生类成员的操作方法,如例3所示。
例3
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 class Animal //定义基类Animal
6 {
7 public:
8 //设置m_nWeightBase属性的成员函数
9 void set_weight(int weight){ m_nWeightBase = weight; }
10 //获取m_nWeightBase值的成员函数
11 int get_weight(){ return m_nWeightBase; }
12 //设置m_nAgeBase属性的成员函数
13 void set_age(int age){ m_nAgeBase = age; }
14 protected:
15 int m_nAgeBase; //定义保护成员m_nAgeBase
16 private:
17 int m_nWeightBase; //定义私有成员m_nWeightBase
18 };
19
20 class Cat:protected Animal //通过保护继承,定义派生类Cat
21 {
22 public:
23 Cat(string con_name):m_strName(con_name){}
24 //定义函数set_print_weight(),该函数访问基类成员函数set_weight()、get_weight()
25 void set_print_weight()
26 {
27 set_weight(6);
28 cout << m_strName << " weight = " << get_weight() << endl;
29 }
30 private:
31 string m_strName; //派生类的私有成员
32 };
33
34 class PersianCat:protected Cat //保护继承的派生类PersianCat
35 {
36 public:
37 PersianCat():Cat("persian cat"){ } //定义构造函数
38 //函数persian_set_print_age()访问Animal类中的数据成员m_nAgeBase和set_age()函数
39 void persian_set_print_age()
40 {
41 set_age(5); //直接调用从基类中保护继承的函数
42 cout << "PersianCat, age = " << m_nAgeBase << endl;
43 }
44 };
45 int main()
46 {
47 PersianCat persian_cat; //定义PersianCat对象
48 persian_cat.persian_set_print_age(); //调用显示m_nAgeBase值的成员函数
49 system("pause");
50 return 0;
51 }
运行结果如图7所示。
图7 例3运行结果
例3中通过两次保护继承,派生出了新类PersianCat,根据保护派生的限制要求,它会限定基类中的public、protected成员在派生类中成为protected成员,因此Animal类中的m_nAgeBase属性及set_age()等函数在新类中可以访问,运行结果正确。
此时,派生类中成员的访问权限,如图8所示。
图8 保护继承方式下派生类各成员属性
若使Cat私有继承自Animal类,PersianCat类私有继承自Cat类,代码如下所示:
class Cat:private Animal //通过私有继承,定义派生类Cat
{
public:
Cat(string con_name):m_strName(con_name){}
//定义函数print_age(),该函数访问基类成员m_nAgeBase
void set_print_age()
{
//正确,set_age()在本类中具有private属性,可以调用
set_age(5);
//正确,age_base在本类中具有private属性,可以访问
cout << m_strName << " age = " << m_nAgeBase << endl;
}
void set_print_weight()
{
set_weight(6);
cout << m_strName << " weight = " << get_weight() << endl;
}
private:
string m_strName; //派生类的私有成员
};
class PersianCat:private Cat //私有继承的派生类PersianCat
{
public:
PersianCat():Cat("persian cat"){ } //定义构造函数
//函数persian_set_print_age()访问Animal类中的数据成员m_nAgeBase和set_age()函数
void persian_set_print_age()
{
//错误,set_age()函数在本类中成为不可访问成员
set_age(5);
//错误,m_nAgeBase在本类中成为不可访问成员
cout << "CPersian_cat, age = " << m_nAgeBase << endl;
}
};
再次编译程序则出现错误,提示如图9所示。
图9 多次私有继承后,派生类访问基类成员出错
此时,派生类中的成员的访问权限,如图10所示。
图10 私有继承方式下多次继承后派生类各成员属性
综上所述,C++中关于类的继承方式对基类成员的访问属性控制如表3所示。
表3 类的继承方式对基类成员的访问属性控制
继承方式 | 基类成员 | |||
---|---|---|---|---|
public**成员** | protected**成员** | private**成员** | 不可访问成员 | |
公有继承 | public成员 | protected成员 | 不可访问成员 | 不可访问成员 |
保护继承 | protected成员 | protected成员 | 不可访问成员 | 不可访问成员 |
私有继承 | private成员 | private成员 | 不可访问成员 | 不可访问成员 |