继承和访问说明符
在本章前面的课程中,您已经了解了基本继承的工作原理。在我们到目前为止的所有示例中,我们都使用了公共继承。也就是说,我们的派生类公开继承基类。
在本课中,我们将详细介绍公共继承,以及另外两种继承(私有和受保护)。我们还将探索不同类型的继承如何与访问说明符交互以允许或限制对成员的访问。
到目前为止,您已经看到了私有和公共访问说明符,它们决定了谁可以访问类的成员。作为一个快速复习,公共成员可以被任何人访问。私有成员员只能通过同一个类或友元函数能访问。这意味着派生类不能直接访问基类的私有成员!
class Base
{
private:
int m_private; // 只能由Base成员和友元函数访问(不是派生类)
public:
int m_public; // 可以被任何人访问
};
这非常简单,你现在应该已经习惯了。
受保护的访问说明符
处理继承的类时,事情会变得复杂一些。
C ++有一个我们尚未讨论的第三个访问说明符,因为它仅在继承上下文中有用。该保护的访问说明符允许成员所属的,友元函数和派生类访问成员的类。但是,受保护的成员无法从类外访问。
class Base
{
public:
int m_public; // 可以被任何人访问
private:
int m_private; // 只能由Base成员和友元函数访问(但不能派生类)
protected:
int m_protected; // c可以由Base成员,有元函数和派生类访问
};
class Derived: public Base
{
public:
Derived()
{
m_public = 1; //allowed:可以从派生类访问公共Base成员
m_private = 2; // not allowed: 无法从派生类访问私有Base成员
m_protected = 3; // allowed: 可以从派生类访问受保护的Base成员
}
};
int main()
{
Base base;
base.m_public = 1; // allowed: 可以从外部类访问公共成员
base.m_private = 2; // not allowed: 无法从外部类访问私有成员
base.m_protected = 3; // not allowed: 无法从外部类访问受保护的成员
}
在上面的示例中,您可以看到受保护的基本成员m_protected可以由派生类直接访问,但不能由公共访问。
那我什么时候应该使用受保护的访问说明符?
使用基类中的受保护属性,派生类可以直接访问该成员。这意味着如果您稍后更改了有关该受保护属性的任何内容(类型,值的含义等等),您可能需要更改基类和所有派生类。
因此,当您(或您的团队)将是从您自己的类派生的那些时,使用受保护的访问说明符是最有用的,并且派生类的数量是合理的。这样,如果您对基类的实现进行了更改,并且因此需要对派生类进行更新,那么您可以自己进行更新(并且不需要永久更新,因为派生类的数量有限)。
使您的成员私有可以为您提供更好的封装,并将派生类与基类的更改隔离开来。但是,构建公共或受保护的接口以支持公共和/或派生类所需的所有访问方法或功能也是成本。这是额外的工作,可能不值得,除非你期望别人是你的类派生的,或者你有大量的派生类,其中更新它们的成本将是昂贵的。
不同种类的继承,以及它们对访问的影响
首先,类有三种不同的方式从其他类继承:public,private和protected。
为此,只需在选择要继承的类时指定所需的访问类型:
// 公共继承Base
class Pub: public Base
{
};
// 私有从Base继承
class Pri: private Base
{
};
// 保护地从Base继承
class Pro: protected Base
{
};
class Def: Base // 默认为私有继承
{
};
如果您不选择继承类型,C ++默认为私有继承(如果您没有另外指定,就像成员默认为私有访问一样)。
这为我们提供了9种组合:3种成员访问说明符(公共,私有和受保护),以及3种继承类型(公共,私有和受保护)。
那么这些之间的区别是什么?简而言之,当继承成员时,可以更改继承成员的访问说明符(仅在派生类中),具体取决于所使用的继承类型。换句话说,在基类中公共或受保护的成员可以更改派生类中的访问说明符。
这看起来有点令人困惑,但并不是那么糟糕。我们将花费本课程的其余部分详细探讨这一点。
在我们逐步完成示例时,请记住以下规则:
#类总是可以访问自己的(非继承的)成员。
#公共访问基于其正在访问的类的访问说明符的类的成员。
#类根据从父类继承的访问说明符访问继承的成员。这取决于访问说明符和使用的继承类型。
公共继承
公共继承是迄今为止最常用的继承类型。事实上,您很少会看到或使用其他类型的继承,因此您的主要关注点应该是理解本节。幸运的是,公共继承也是最容易理解的。当您公开继承基类时,继承的公共成员保持公开,并且继承的受保护成员保持受保护。并且无法访问的继承私有成员无法访问。
基类中的访问说明符 公开继承时的访问说明符
基类中的访问说明符 | 公共继承时的访问说明符 |
---|---|
public | 公共访问 |
private | 无法访问 |
protected | 受保护 |
这是一个展示工作原理的例子:
class Base
{
public:
int m_public;
private:
int m_private;
protected:
int m_protected;
};
class Pub: public Base // 注意:公共继承
{
// 公共继承意味着:
// 公共继承成员保持公开(所以m_public被视为公共)
//受保护的继承成员保持受保护(因此m_protected被视为受保护)
//私有继承的成员无法访问(因此m_private无法访问)
public:
Pub()
{
m_public = 1; // okay:m_public被继承为公共的
m_private = 2; // not okay: m_private无法从派生类中访问
m_protected = 3; // okay:m_protected继承为protected
}
};
int main()
{
// 外部访问使用正在访问的类的访问说明符。
Base base;
base.m_public = 1; // okay: m_public在Base是公开的
base.m_private = 2; // not okay: m_private在Base中是私有的
base.m_protected = 3; // not okay: m_protected在Base中受到保护
Pub pub;
pub.m_public = 1; // okay: m_public在Pub是公开的
pub.m_private = 2; // not okay: m_private在Pub不能访问
pub.m_protected = 3; // not okay: m_protected在Pub中受到保护
}
这与上面我们引入受保护的访问说明符的示例相同,只是我们已经实例化了派生类,只是为了表明在公共继承中,事物在基类和派生类中的工作方式相同。
公共继承是你应该使用的,除非你有特殊的理由不这样做。
规则:除非您有特殊原因,否则请使用公共继承。
私有继承
使用私有继承,基类中的所有成员都将作为私有继承。这意味着私有成员保持私密,受保护的公共成员变得私密。
请注意,这不会影响派生类访问从其父级继承的成员的方式!它只影响试图通过派生类访问这些成员的代码。
class Base
{
public:
int m_public;
private:
int m_private;
protected:
int m_protected;
};
class Pri: private Base // 注意:私有继承
{
// 私有继承意味着:
// 公共继承成员变为私有(因此m_public被视为私有)
// 受保护的继承成员变为私有(因此m_protected被视为私有)
//私有继承的成员无法访问(因此m_private无法访问)
public:
Pri()
{
m_public = 1; // okay: m_public现在在Pri中是私有的
m_private = 2; // not okay: 派生类无法访问基类中的私有成员
m_protected = 3; // okay:m_protected现在在Pri中是私有的
}
};
int main()
{
// 外部访问使用正在访问的类的访问说明符。
//在这种情况下,base的访问说明符。
Base base;
base.m_public = 1; // okay: m_public在Base是公开的
base.m_private = 2; // not okay: m_private在Base中是私有的
base.m_protected = 3; // not okay: m_protected在Base中受到保护
Pri pri;
pri.m_public = 1; // not okay: m_public在 Pri是私有的
pri.m_private = 2; // not okay: Pri中无法访问m_private
pri.m_protected = 3; // not okay: m_protected在Pri是私有的
}
以表格形式总结:
基类中的访问说明符 私有继承时的访问说明符
基类中的访问说明符 | 私有继承时的访问说明符 |
---|---|
public | 私有的 |
private | 无法访问 |
protected | 私有的 |
当派生类与基类没有明显关系,但在内部使用基类进行实现时,私有继承会很有用。在这种情况下,我们可能不希望基类的公共接口通过派生类的对象公开(就像我们公开继承的那样)。
实际上,很少使用私有继承。
受保护的继承
受保护的继承是最后一种继承方法。它几乎从未使用过,除非在非常特殊的情况下。通过受保护的继承,公共成员和受保护成员将受到保护,私有成员将无法访问。
因为这种形式的继承是如此罕见,我们将跳过这个例子,并用表格总结:
基类中的访问说明符 受保护地继承时的访问说明符
基类中的访问说明符 | 受保护继承时的访问说明符 |
---|---|
public | 受保护 |
private | 无法访问 |
protected | 受保护 |
最后一个例子
class Base
{
public:
int m_public;
private:
int m_private;
protected:
int m_protected;
};
Base可以不受限制地访问自己的成员。公众只能访问m_public。派生类可以访问m_public和m_protected。
class D2 : private Base // 注意: 私有继承
{
// 私有继承意味着:
// 公共继承成员变得私有
// 受保护的继承成员变为私有成员
// 私有继承的成员无法访问
public:
int m_public2;
private:
int m_private2;
protected:
int m_protected2;
};
D2可以不受限制地访问自己的成员。D2可以访问Base的m_public和m_protected成员,但不能访问m_private。由于D2私有地继承了Base,因此当通过D2访问时,m_public和m_protected现在被视为私有。这意味着公共在使用D2对象时不能访问这些变量,也不能从D2派生任何类。
class D3 : public D2
{
// 公共继承意味着:
// 公共继承成员保持公开
// 受保护的继承成员依然受保护
// 私有继承的成员无法访问
public:
int m_public3;
private:
int m_private3;
protected:
int m_protected3;
};
D3可以不受限制地访问自己的成员。D3可以访问D2的m_public2和m_protected2成员,但不能访问m_private2。由于D3公开继承了D2,因此m_public2和m_protected2在通过D3访问时保留其访问说明符。D3无法访问Base的m_private,它在Base中已经是私有的。它也无法访问Base的m_protected或m_public,当D2继承它们时,它们都变成了私有的。
Summary:
访问说明符,继承类型和派生类交互的方式会导致很多混乱。我尽可能地尝试澄清事情:
首先,类(和友元)总是可以访问自己的非继承成员。访问说明符仅影响外部和派生类是否可以访问这些成员。
其次,当派生类继承成员时,这些成员可以更改派生类中的访问说明符。这不会影响派生类自己的(非继承的)成员(具有自己的访问说明符)。它只会影响派生类派生的外部和类是否可以访问这些继承的成员。
这是一个包含所有访问说明符和继承类型组合的表:
基类中的访问说明符 公开继承时的访问说明符 私有继承时的访问说明符 受保护地继承时的访问说明符
基类中的访问说明符 | 公共继承时的访问说明符 | 私有继承时的访问说明符 | 受保护地继承时的访问说明符 |
---|---|---|---|
public | 公共 | 私有的 | 受保护 |
private | 无法访问 | 无法访问 | 无法访问 |
protected | 受保护 | 私有的 | 受保护 |
最后请注意,尽管在上面的示例中,我们仅显示了使用成员变量的示例,但这些访问规则适用于所有成员(例如,在类中声明的成员函数和类型)。