In the previous lessons in this chapter, you’ve learned a bit about how base inheritance works. In all of our examples so far, we’ve used public inheritance. That is, our derived class publicly inherits the base class.
In this lesson, we’ll take a closer look at public inheritance, as well as the two other kinds of inheritance (private and protected). We’ll also explore how the different kinds of inheritance interact with access specifiers to allow or restrict access to members.
在这一节我们要研究一下public继承,还有两个其他类型的继承(private和protected), 我们也会探究不同类型的继承和访问运算符是怎么配合起来工作,来达到限制或者允许对成员的访问的。
To this point, you’ve seen the private and public access specifiers, which determine who can access the members of a class. As a quick refresher, public members can be accessed by anybody. Private members can only be accessed by member functions of the same class or friends. This means derived classes can not access private members of the base class directly!
你已经见过private和public访问限定符了,这两个限定符决定了谁能访问某个类的成员。快速的回忆一下,public成员可以被所有的地方访问,而private成员只能被同属于一个类的成员函数访问。这意味着派生类不能直接访问基类的私有成员。
class Base
{
private:
int m_private; // can only be accessed by Base members and friends (not derived classes)
public:
int m_public; // can be accessed by anybody
};
This is pretty straightforward, and you should be quite used to it by now.
这很简单
When dealing with inherited classes, things get a bit more complex.
在处理被继承的类的时候,事情变得有一点麻烦。
C++ has a third access specifier that we have yet to talk about because it’s only useful in an inheritance context. The protected access specifier allows the class the member belongs to, friends, and derived classes to access the member. However, protected members are not accessible from outside the class.
C++有第三个访问限定符,叫protected。这个访问限定符我们此前从未提及。protected访问限定符允许属于这个类的所有成员函数,友元函数,派生类访问这个成员,然而,protected函数不能从继承链的外面被访问。
class Base
{
public:
int m_public; // can be accessed by anybody
protected:
int m_protected; // can be accessed by Base members, friends, and derived classes
private:
int m_private; // can only be accessed by Base members and friends (but not derived classes)
};
class Derived: public Base
{
public:
Derived()
{
m_public = 1; // allowed: can access public base members from derived class
m_protected = 2; // allowed: can access protected base members from derived class
m_private = 3; // not allowed: can not access private base members from derived class
}
};
int main()
{
Base base;
base.m_public = 1; // allowed: can access public members from outside class
base.m_protected = 2; // not allowed: can not access protected members from outside class
base.m_private = 3; // not allowed: can not access private members from outside class
}
In the above example, you can see that the protected base member m_protected is directly accessible by the derived class, but not by the public.
在上面的例子中,你可以看到被保护的成员是可以被派生类直接访问的
So when should I use the protected access specifier?
什么时候我应该用protected限定符呢?
With a protected attribute in a base class, derived classes can access that member directly. This means that if you later change anything about that protected attribute (the type, what the value means, etc…), you’ll probably need to change both the base class AND all of the derived classes.
基类中的protected限定的成员,所有的派生类都可以直接的访问。这意味如果你之后修改了任何关于这个protected限定的成员,你可能需要修改基类和所有派生的类。
Therefore, using the protected access specifier is most useful when you (or your team) are going to be the ones deriving from your own classes, and the number of derived classes is reasonable. That way, if you make a change to the implementation of the base class, and updates to the derived classes are necessary as a result, you can make the updates yourself (and have it not take forever, since the number of derived classes is limited).
因此protected访问运算符在你派生自己的类的时候,或者是派生类的数量比较的合理的情况下,才有用。在那种情况下,如果你对基类进行了修改,而且也更新了必要的派生类的内容。你可以自己做完这些事情。(也不用花太多时间,因为这个派生类数量是比较少的。)
Making your members private gives you better encapsulation and insulates derived classes from changes to the base class. But there’s also a cost to build a public or protected interface to support all the access methods or capabilities that the public and/or derived classes need. That’s additional work that’s probably not worth it, unless you expect someone else to be the one deriving from your class, or you have a huge number of derived classes, where the cost of updating them all would be expensive.
但是如果用private的访问限定符的话,会让你更好的封装和把派生类隔开。但是也有相应的代价,你需要开销一个public或者protected的接口来支持你的派生类的需要。那可能是不值得做的事情,除非你想到可能会有别人来继承你写的基类。或者你有很多的基类。你需要更新很多的东西。(所以听这个话音是用protect还比较好)
不同的继承类型对于访问控制的影响
First, there are three different ways for classes to inherit from other classes: public, protected, and private.
首先,有三种方法来继承别的类:public,protected,private。
To do so, simply specify which type of access you want when choosing the class to inherit from:
只需要在选择类去继承的时候,简单地指定你想要的访问类型。
// Inherit from Base publicly
class Pub: public Base
{
};
// Inherit from Base protectedly
class Pro: protected Base
{
};
// Inherit from Base privately
class Pri: private Base
{
};
class Def: Base // Defaults to private inheritance
{
};
If you do not choose an inheritance type, C++ defaults to private inheritance (just like members default to private access if you do not specify otherwise).
如果你不选择继承类型,C++默认你选择private继承。
That gives us 9 combinations: 3 member access specifiers (public, private, and protected), and 3 inheritance types (public, private, and protected).
这给了我们9种组合,3x3. 三个成员访问限定符和三个继承类型。
So what’s the difference between these? In a nutshell, when members are inherited, the access specifier for an inherited member may be changed (in the derived class only) depending on the type of inheritance used. Put another way, members that were public or protected in the base class may change access specifiers in the derived class.
所以九种组合的差别是什么呢?简言之,在成员被继承的过程中,访问限定符(被继承的那个类中的)限定的成员的访问权限会发生变化,取决于你选择哪种继承类型。换句话说,public或者是protect继承可能会改变原来在基类中的成员的访问权限。基类中的成员原来是被A限定,后来变成了被B限定。
This might seem a little confusing, but it’s not that bad. We’ll spend the rest of this lesson exploring this in detail.
这看起来可能有点让人困惑,但是这样做有这样做的好处。我们用这节的剩下的部分来研究这些事情。
Keep in mind the following rules as we step through the examples:
在我们继续看下面的例子之前,先把以下内容记在脑子里
-
A class can always access its own (non-inherited) members.
一个类总是可以访问自己的成员。
-
The public accesses the members of a class based on the access specifiers of the class it is accessing.
大家(在继承链外的部分)在访问一个类的成员的访问权限,取决于成员在这个类中的访问限定符
-
A derived class accesses inherited members based on the access specifier inherited from the parent class. This varies depending on the access specifier and type of inheritance used.
派生类对它继承的成员变量的访问,是由父类中访问限定符决定的。这个事情会根据访问限定符和继承类型发生变化
Public继承
Public inheritance is by far the most commonly used type of inheritance. In fact, very rarely will you see or use the other types of inheritance, so your primary focus should be on understanding this section. Fortunately, public inheritance is also the easiest to understand. When you inherit a base class publicly, inherited public members stay public, and inherited protected members stay protected. Inherited private members, which were inaccessible because they were private in the base class, stay inaccessible.
Public是我们到目前为止看到最多的继承类型。事实上,你也很少会见到别的类型的继承。所以你主要关注应该放在这一部分上。幸运的事情是public继承也是最容易理解的继承。原来在父类中是public的,现在还是public。原来在父类中是protected的,现在还是protected。原来在父类中是private的你没法碰,现在你还是没办法碰,别人也没法碰。
Access specifier in base class | Access specifier when inherited publicly |
---|---|
Public | Public |
Protected | Protected |
Private | Inaccessible |
Here’s an example showing how things work:
这里是介绍这个事情的一个例子
class Base
{
public:
int m_public;
protected:
int m_protected;
private:
int m_private;
};
class Pub: public Base // note: public inheritance
{
// Public inheritance means:
// Public inherited members stay public (so m_public is treated as public)
// Protected inherited members stay protected (so m_protected is treated as protected)
// Private inherited members stay inaccessible (so m_private is inaccessible)
public:
Pub()
{
m_public = 1; // okay: m_public was inherited as public
m_protected = 2; // okay: m_protected was inherited as protected
m_private = 3; // not okay: m_private is inaccessible from derived class
}
};
int main()
{
// Outside access uses the access specifiers of the class being accessed.
Base base;
base.m_public = 1; // okay: m_public is public in Base
base.m_protected = 2; // not okay: m_protected is protected in Base
base.m_private = 3; // not okay: m_private is private in Base
Pub pub;
pub.m_public = 1; // okay: m_public is public in Pub
pub.m_protected = 2; // not okay: m_protected is protected in Pub
pub.m_private = 3; // not okay: m_private is inaccessible in Pub
This is the same as the example above where we introduced the protected access specifier, except that we’ve instantiated the derived class as well, just to show that with public inheritance, things work identically in the base and derived class.
这个例子和我们先前的例子是一样的。除了我们也实例化了派生类,只是用来说明这个公开继承的事情。事情在基类和派生类中是一样的。
Public inheritance is what you should be using unless you have a specific reason not to.
public继承是你应该选取的继承类型,除非你有使用别的继承类型的特定理由。
Rule : Use public inheritance unless you have a specific reason to do otherwise.
Protected inheritance is the least common method of inheritance. It is almost never used, except in very particular cases. With protected inheritance, the public and protected members become protected, and private members stay inaccessible.
protected继承是最不常见的一种继承类型,它基本上从来不会被用到。除了极其特殊的情况。在protected的继承下,public和protected成员变成了protected成员,private成员依然还是你,不能碰。
Because this form of inheritance is so rare, we’ll skip the example and just summarize with a table:
因为这种形式的继承是这么少见,我们没有例子了。直接总结它吧。
Access specifier in base class | Access specifier when inherited protectedly |
---|---|
Public | Protected |
Protected | Protected |
Private | Inaccessible |
With private inheritance, all members from the base class are inherited as private. This means private members stay private, and protected and public members become private.
在private继承类型下, protected和public变成了private,而在父类中是private的成员现在还是private。
Note that this does not affect the way that the derived class accesses members inherited from its parent! It only affects the code trying to access those members through the derived class.
注意这并不会改变父类中的访问控制,这只影响试图去访问派生类对象的成员的那些代码。
class Base
{
public:
int m_public;
protected:
int m_protected;
private:
int m_private;
};
class Pri: private Base // note: private inheritance
{
// Private inheritance means:
// Public inherited members become private (so m_public is treated as private)
// Protected inherited members become private (so m_protected is treated as private)
// Private inherited members stay inaccessible (so m_private is inaccessible)
public:
Pri()
{
m_public = 1; // okay: m_public is now private in Pri
m_protected = 2; // okay: m_protected is now private in Pri
m_private = 3; // not okay: derived classes can't access private members in the base class
}
};
int main()
{
// Outside access uses the access specifiers of the class being accessed.
// In this case, the access specifiers of base.
Base base;
base.m_public = 1; // okay: m_public is public in Base
base.m_protected = 2; // not okay: m_protected is protected in Base
base.m_private = 3; // not okay: m_private is private in Base
Pri pri;
pri.m_public = 1; // not okay: m_public is now private in Pri
pri.m_protected = 2; // not okay: m_protected is now private in Pri
pri.m_private = 3; // not okay: m_private is inaccessible in Pri
return 0;
}
To summarize in table form:
Access specifier in base class | Access specifier when inherited privately |
---|---|
Public | Private |
Protected | Private |
Private | Inaccessible |
Private inheritance can be useful when the derived class has no obvious relationship to the base class, but uses the base class for implementation internally. In such a case, we probably don’t want the public interface of the base class to be exposed through objects of the derived class (as it would be if we inherited publicly).
私有继承在想要使用基类的功能,但是又没有明显的关系的情况下有用。在这种情况下,我们不想把基类的任何接口通过派生类实例化的对象暴露出来。
In practice, private inheritance is rarely used.
在实践中,私有继承也是很少被使用的。
class Base
{
public:
int m_public;
protected:
int m_protected;
private:
int m_private;
};
Base can access its own members without restriction. The public can only access m_public. Derived classes can access m_public and m_protected.
基类可以访问自己的成员而不受到任何限制。外界只能访问公开的成员,派生类可以访问其public和protected成员。
class D2 : private Base // note: private inheritance
{
// Private inheritance means:
// Public inherited members become private
// Protected inherited members become private
// Private inherited members stay inaccessible
public:
int m_public2;
protected:
int m_protected2;
private:
int m_private2;
};
D2 can access its own members without restriction. D2 can access Base’s m_public and m_protected members, but not m_private. Because D2 inherited Base privately, m_public and m_protected are now considered private when accessed through D2. This means the public can not access these variables when using a D2 object, nor can any classes derived from D2.
D2在访问自己的成员的时候不会受到任何限制。D2可以访问基类的public和protected成员,但是private还是不能碰(也许能理解为没有继承下来,爸爸私有的不给儿子的东西)。因为是私有继承,所以public成员和protected成员现在变成了私有成员。在派生类中随便访问,但是到了外界,就不能通过派生类对象访问了。而且派生类的派生类也不能访问。
class D3 : public D2
{
// Public inheritance means:
// Public inherited members stay public
// Protected inherited members stay protected
// Private inherited members stay inaccessible
public:
int m_public3;
protected:
int m_protected3;
private:
int m_private3;
};
D3 can access its own members without restriction. D3 can access D2’s m_public2 and m_protected2 members, but not m_private2. Because D3 inherited D2 publicly, m_public2 and m_protected2 keep their access specifiers when accessed through D3. D3 has no access to Base’s m_private, which was already private in Base. Nor does it have access to Base’s m_protected or m_public, both of which became private when D2 inherited them.
The way that the access specifiers, inheritance types, and derived classes interact causes a lot of confusion. To try and clarify things as much as possible:
继承类型容易把人搞晕。尽量试着把东西弄明白。
First, a class (and friends) can always access its own non-inherited members. The access specifiers only affect whether outsiders and derived classes can access those members.
第一件事情是一个类总是可以访问自己的成员。访问限定符只会影响类外对它自身的访问。
Second, when derived classes inherit members, those members may change access specifiers in the derived class. This does not affect the derived classes’ own (non-inherited) members (which have their own access specifiers). It only affects whether outsiders and classes derived from the derived class can access those inherited members.
第二件事情是当一个派生类继承成员的时候,那些成员可能会改变在派生类中的访问限定符。但是这不会改变派生类自己的成员的访问限定。只会影响那些继承下来的成员。所以继承类型实际上是对继承过来的内容的限定符进行了一次改写。
Here’s a table of all of the access specifier and inheritance types combinations:
Access specifier in base class | Access specifier when inherited publicly | Access specifier when inherited privately | Access specifier when inherited protectedly |
---|---|---|---|
Public | Public | Private | Protected |
Protected | Protected | Private | Protected |
Private | Inaccessible | Inaccessible | Inaccessible |
As a final note, although in the examples above, we’ve only shown examples using member variables, these access rules hold true for all members (e.g. member functions and types declared inside the class).
最后要说一句,尽管在上面的例子中我们只展示了那些只拥有成员变量的类,但是我们描述的这些规则对于函数来说也是适用的。 :-)