公共继承、私有继承和受保护的继承有什么区别?

What is the difference between public, private, and protected inheritance?

提问人: 提问时间:5/14/2009 最后编辑:Jan Schultke 更新时间:9/17/2023 访问量:765206

问:

C++ 中的 、 和继承有什么区别?publicprivateprotected

C 继承 封装 access-specifier c++-faq

评论


答:

70赞 Doug T. 5/14/2009 #1

它与基类的公共成员如何从派生类中公开有关。

  • public -> 基类的公共成员将是公共的(通常是默认的)
  • protected -> 基类的公共成员将受到保护
  • private -> 基类的公共成员将是私有的

正如 litb 所指出的,公共继承是大多数编程语言中都会看到的传统继承。也就是说,它模拟了“IS-A”关系。私有继承是 C++ 特有的 AFAIK,是一种“在方面实现”的关系。也就是说,您希望在派生类中使用公共接口,但不希望派生类的用户有权访问该接口。许多人认为,在这种情况下,您应该聚合基类,也就是说,不要将基类作为私有基,而是在 derived 的成员中加入,以便重用基类的功能。

评论

13赞 Johannes Schaub - litb 5/14/2009
最好说“公开:遗产会被大家看到”。protected:继承将仅由派生类和朋友看到“,”private:继承将仅由类本身和朋友看到”。这与你的措辞不同,因为不仅成员可以不可见,而且 IS-A 关系也可以不可见。
4赞 Rich 4/23/2010
我使用私有继承的一次是做 Doug T 所描述的,即“你想在派生类中使用公共接口,但不希望派生类的用户有权访问该接口”。我基本上用它来密封旧接口,并通过派生类公开另一个接口。
1197赞 Anzurio 5/14/2009 #2

为了回答这个问题,我想先用我自己的话来描述成员的访问器。如果您已经知道这一点,请跳到标题“下一步:”。

我知道有三个访问器:和 。publicprotectedprivate

让:

class Base {
    public:
        int publicMember;
    protected:
        int protectedMember;
    private:
        int privateMember;
};
  • 所有知道的东西也知道包含.BaseBasepublicMember
  • 只有孩子(和他们的孩子)知道包含 .BaseprotectedMember
  • 没有人知道.BaseprivateMember

我所说的“知道”是指“承认存在,从而能够访问”。

下一个:

公共、私人和受保护的继承也是如此。让我们考虑一个类和一个继承自 的类。BaseChildBase

  • 如果继承是 ,则所有知道并且也知道继承自 的所有东西。publicBaseChildChildBase
  • 如果继承是 ,则只有 ,并且其子项知道它们继承自 。protectedChildBase
  • 如果继承是 ,则除了其他人之外,没有人知道继承。privateChild

评论

224赞 Zhe Chen 4/27/2015
我想补充几句话,C++ 中的可见性基于类而不是对象,这意味着同一类的对象可以不受限制地访问彼此的私有字段。
59赞 The Vivandiere 6/26/2015
如果你很难理解这一点,请阅读 Kirill V. Lyadvinsky 的回答,然后回来阅读这篇文章。
6赞 underscore_d 2/28/2016
这只是另一个案例,它说明了在大多数情况下,继承方式就像硬编码的方式一样,用于编写 类型的匿名成员。与任何其他成员一样,它有一个访问说明符,它对外部访问施加相同的控制。SomeBaseSomeBase
2赞 gen 7/13/2016
@ZheChen如果我有对象 Tom 和 Jerry 类 Person with private field age,您如何访问(和修改?杰瑞使用汤姆的年龄?
2赞 lineil 9/11/2018
你能说明一下你所说的“意识到'继承'”是什么意思吗?我能理解“我可以访问这个,我不能访问那个”,但当有人说“我知道 A 继承了 B 的继承”时,我不明白,我在这里做什么,我在检查继承吗?
3赞 Dan Olson 5/14/2009 #3

它实质上是对派生类中基类的公共成员和受保护成员的访问保护。通过公共继承,派生类可以查看基的公共成员和受保护成员。对于私人继承,它不能。使用 protected,派生类和从该类派生的任何类都可以看到它们。

9赞 Andrew Noyes 5/14/2009 #4

受保护的数据成员可由从类继承的任何类访问。但是,私人数据成员不能。假设我们有以下内容:

class MyClass {
    private:
        int myPrivateMember;    // lol
    protected:
        int myProtectedMember;
};

在此类的扩展中,引用将不起作用。但是,会。该值仍然是封装的,因此,如果我们有一个名为 的此类的实例化,则将不起作用,因此它在功能上类似于私有数据成员。this.myPrivateMemberthis.myProtectedMembermyObjmyObj.myProtectedMember

7赞 Roee Adler 5/14/2009 #5

总结:

  • 私有:除了在班级内,没有人能看到它
  • 受保护:私有 + 派生类可以看到它
  • 公众:全世界都能看见

继承时,您可以(在某些语言中)按特定方向更改数据成员的保护类型,例如,从受保护更改为公共。

10赞 Arkaitz Jimenez 9/3/2009 #6

如果你从另一个类公开继承,每个人都知道你正在继承,并且任何人都可以通过基类指针多态地使用你。

如果你以受保护的方式继承,则只有你的子类才能多态地使用你。

如果私下继承,则只有您自己才能执行父类方法。

这基本上象征着其他班级对你与父班级的关系的了解

1760赞 Kirill V. Lyadvinsky 9/3/2009 #7
class A 
{
    public:
       int x;
    protected:
       int y;
    private:
       int z;
};

class B : public A
{
    // x is public
    // y is protected
    // z is not accessible from B
};

class C : protected A
{
    // x is protected
    // y is protected
    // z is not accessible from C
};

class D : private A    // 'private' is default for classes
{
    // x is private
    // y is private
    // z is not accessible from D
};

重要提示:B、C 和 D 类都包含变量 x、y 和 z。这只是访问问题。

关于受保护继承和私有继承的使用,您可以阅读 此处.

评论

49赞 Iwillnotexist Idonotexist 3/4/2015
Anzurio 所写的内容仅与您的答案一起点击。Плус 1.
3赞 tjwrona1992 8/3/2017
我对它如何工作的理解还很遥远!非常感谢您的澄清。
1赞 Chan Kim 4/26/2019
我花了一些时间才明白这一点。但现在很清楚了。谢谢!
3赞 John Leuenhagen 7/6/2020
关于“private”是类的默认值的好说明。
1赞 Badhan Sen 6/17/2021
奇妙的解释。到目前为止,这里一切都很清楚。
20赞 sbi 9/3/2009 #8

公共继承对 IS-A 关系进行建模。跟

class B {};
class D : public B {};

every 是一个 .DB

私有继承对 IS-IMPLEMENTED-USING 关系(或任何名称)进行建模。跟

class B {};
class D : private B {};

a 不是 a,但每个 it 都在其实现中使用它。始终可以通过改用包含来消除私有继承:DBDB

class B {};
class D {
  private: 
    B b_;
};

这也可以使用 来实现,在这种情况下,使用其 .与继承相比,包含是类型之间的紧密耦合,因此通常应首选它。有时,使用包含而不是私有继承不如私有继承方便。这通常是懒惰的蹩脚借口。DBb_

我认为没有人知道什么是继承模型。至少我还没有看到任何令人信服的解释。protected

评论

0赞 user4951 11/28/2013
有人说是一种关系。就像用椅子当锤子一样。这里椅子:受保护的锤子
0赞 Destructor 11/6/2015
当使用包含而不是私有继承不如私有继承方便时?你能用一个例子来解释一下吗?
1赞 sbi 11/6/2015
@Pravasi:如果私下派生自 ,它可以覆盖 的虚函数。(例如,如果是一个观察者接口,那么可以实现它并传递给需要 auch 接口的函数,而每个人都不能用作观察者。此外,可以通过执行以下操作有选择地使成员在其界面中可用。两者在语法上都不方便在 is a member 时实现。DDBBDthisDDBusing B::memberB
0赞 lorro 4/8/2017
@sbi:老的,但是......在 CRTP 和/或虚拟的情况下,遏制是不行的(正如您在评论中正确描述的那样 - 但这意味着如果 B 具有抽象方法并且您不被允许触摸它,则不能将其建模为遏制)。 我发现继承对基类和 ctor 很有用:protectedvirtualprotectedstruct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
129赞 Johannes Schaub - litb 9/4/2009 #9

限制继承的可见性将使代码无法看到某个类继承了另一个类:从派生到基的隐式转换将不起作用,从基到派生的隐式转换也不起作用。static_cast

只有类的成员/好友才能看到私有继承,只有成员/好友和派生类才能看到受保护的继承。

公共继承

  1. IS-A 继承。按钮是窗口,任何需要窗口的地方,也可以传递按钮。

    class button : public window { };
    

受保护的继承

  1. 受保护的实现方式。很少有用。用于从空类派生并使用空基类优化节省内存(以下示例不使用模板来保持该点):boost::compressed_pair

    struct empty_pair_impl : protected empty_class_1 
    { non_empty_class_2 second; };
    
    struct pair : private empty_pair_impl {
      non_empty_class_2 &second() {
        return this->second;
      }
    
      empty_class_1 &first() {
        return *this; // notice we return *this!
      }
    };
    

私人继承

  1. 就此实施而言。基类的用法仅用于实现派生类。对于特征和大小很重要(仅包含函数的空特征将利用空基类优化)。不过,遏制通常是更好的解决方案。字符串的大小至关重要,因此在这里经常看到这种用法

    template<typename StorageModel>
    struct string : private StorageModel {
    public:
      void realloc() {
        // uses inherited function
        StorageModel::realloc();
      }
    };
    

公众成员

  1. 骨料

    class pair {
    public:
      First first;
      Second second;
    };
    
  2. 访问

    class window {
    public:
        int getWidth() const;
    };
    

受保护成员

  1. 为派生类提供增强的访问

    class stack {
    protected:
      vector<element> c;
    };
    
    class window {
    protected:
      void registerClass(window_descriptor w);
    };
    

私人会员

  1. 保留实施详细信息

    class window {
    private:
      int width;
    };
    

请注意,C 样式强制转换特意允许以定义和安全的方式将派生类强制转换为受保护或私有基类,并强制转换为其他方向。应该不惜一切代价避免这种情况,因为它可以使代码依赖于实现细节 - 但如有必要,您可以使用此技术。

评论

7赞 DangerMouse 9/14/2012
我认为斯科特·迈尔斯(尽管我喜欢他的东西)对普遍的困惑有很多要回答。我现在认为他对 IS-A 和 IS-IMPLEMENTED-IN-TERMS-OF 的类比足以应对正在发生的事情。
7赞 varun 1/6/2010 #10

私人:

基类的私有成员只能由该基类的成员访问。

公共:

基类的公共成员可以由该基类的成员、其派生类的成员以及基类和派生类之外的成员访问。

保护:

基类的受保护成员可由基类的成员及其派生类的成员访问。


总之:

private:基础

受保护:基础 + 派生

public:基数 + 派生 + 任何其他成员

38赞 kinshuk4 9/9/2010 #11
Member in base class : Private   Protected   Public   

继承类型对象继承为

Private            :   Inaccessible   Private     Private   
Protected          :   Inaccessible   Protected   Protected  
Public             :   Inaccessible   Protected   Public

评论

23赞 Sam Kauffman 4/12/2013
这种误导。基类的私有成员的行为与普通的私有类成员完全不同,它们根本无法从派生类中访问。我认为你的三个“私人”列应该是“无法访问”的列。参见基里尔·李亚德文斯基(Kirill V. Lyadvinsky)对这个问题的回答。
4赞 Prajosh Premdas 2/24/2015 #12

我找到了一个简单的答案,所以也想把它发布出来供我将来参考。

它来自链接 http://www.learncpp.com/cpp-tutorial/115-inheritance-and-access-specifiers/

class Base
{
public:
    int m_nPublic; // can be accessed by anybody
private:
    int m_nPrivate; // can only be accessed by Base member functions (but not derived classes)
protected:
    int m_nProtected; // can be accessed by Base member functions, or derived classes.
};

class Derived: public Base
{
public:
    Derived()
    {
        // Derived's access to Base members is not influenced by the type of inheritance used,
        // so the following is always true:

        m_nPublic = 1; // allowed: can access public base members from derived class
        m_nPrivate = 2; // not allowed: can not access private base members from derived class
        m_nProtected = 3; // allowed: can access protected base members from derived class
    }
};

int main()
{
    Base cBase;
    cBase.m_nPublic = 1; // allowed: can access public members from outside class
    cBase.m_nPrivate = 2; // not allowed: can not access private members from outside class
    cBase.m_nProtected = 3; // not allowed: can not access protected members from outside class
}
11赞 Enissay 5/10/2015 #13
Accessors    | Base Class | Derived Class | World
—————————————+————————————+———————————————+———————
public       |      y     |       y       |   y
—————————————+————————————+———————————————+———————
protected    |      y     |       y       |   n
—————————————+————————————+———————————————+———————
private      |            |               |    
  or         |      y     |       n       |   n
no accessor  |            |               |

y: accessible
n: not accessible

基于这个 java 示例...我认为一张小桌子胜过千言万语:)

评论

0赞 Zelldon 6/12/2015
Java 只有公共继承
0赞 Enissay 6/13/2015
这不是谈论 java 的话题,但不,你错了......有关详细信息,请点击我上面答案中的链接
1赞 Zelldon 6/13/2015
你提到了java,所以它是主题。您的示例处理了在 jaca 中使用的说明符。问题在于继承的说明符,这在 Java 中是不存在的,并且有所作为。如果超类中的字段是公共的,而继承是私有的,则该字段只能在子类内部访问。在外部,没有指示子类是否扩展了超类。但您的表只解释了字段和方法的说明符。
92赞 BugShotGG 6/8/2015 #14

这三个关键字也用于完全不同的上下文中,以指定可见性继承模型

此表收集了组件声明和继承模型的所有可能组合,这些模型显示了在完全定义子类时对组件的最终访问。

enter image description here

上表的解释方式如下(请看第一行):

如果将组件声明为 public,并且其类被继承为 public,则生成的访问public

举个例子:

 class Super {
    public:      int p;
    private:     int q;
    protected:   int r;
 };

 class Sub : private Super {};

 class Subsub : public Sub {};

在类 Subsub 中,变量 , 的结果访问为 nonepqr

另一个例子:

class Super {
    private:     int x;
    protected:   int y;
    public:      int z;
 };
class Sub : protected Super {};

在类 Sub 中,变量 的最终访问是受保护的,而变量的访问是 noneyzx

一个更详细的例子:

class Super {
private:
    int storage;
public:
    void put(int val) { storage = val;  }
    int  get(void)    { return storage; }
};
int main(void) {
    Super object;

    object.put(100);
    object.put(object.get());
    cout << object.get() << endl;
    return 0;
}

现在让我们定义一个子类:

class Sub : Super { };

int main(void) {
    Sub object;

    object.put(100);
    object.put(object.get());
    cout << object.get() << endl;
    return 0;
}

名为 Sub 的定义类是 named 或该类的子类,派生自该类。 该类既不引入新变量,也不引入新函数。这是否意味着类的任何对象都继承了该类实际上是类对象的副本之后的所有特征?SuperSubSuperSubSubSuperSuper

不可以。事实并非如此。

如果我们编译以下代码,我们只会得到编译错误,说方法不可访问。为什么?putget

当我们省略可见性说明符时,编译器假定我们将应用所谓的私有继承。这意味着所有公共超类组件都变成了私有访问,私有超类组件将根本无法访问。因此,这意味着您不能在子类中使用后者。

我们必须通知编译器,我们要保留以前使用的访问策略。

class Sub : public Super { };

不要被误导:这并不意味着超级的私人组件 类(如存储变量)将在 有点神奇的方式。私有组件将保持私有,公共组件将保持公共

类的对象可以“几乎”做与从该类创建的哥哥兄弟姐妹相同的事情。 “几乎”是因为作为子类的事实也意味着该类失去了对超类的私有组件的访问。我们不能编写能够直接操作存储变量的类的成员函数。SubSuperSub

这是一个非常严重的限制。有什么解决方法吗?

是的

第三个访问级别称为受保护。关键字 protected 意味着标有它的组件在被任何子类使用时的行为类似于公共组件,并且在世界其他地方看起来就像一个私有组件。-- 这仅适用于公开继承的类(如我们示例中的 Super 类) --

class Super {
protected:
    int storage;
public:
    void put(int val) { storage = val;  }
    int  get(void)    { return storage; }
};

class Sub : public Super {
public:
    void print(void) {cout << "storage = " << storage;}
};

int main(void) {
    Sub object;

    object.put(100);
    object.put(object.get() + 1);
    object.print();
    return 0;
}

正如您在示例代码中看到的,我们为该类提供了新功能,它做了一件重要的事情:它从 Super 类访问存储变量Sub

如果将变量声明为私有变量,则不可能这样做。 在 main 函数作用域中,变量无论如何都是隐藏的,所以如果你写任何内容,比如:

object.storage = 0;

编译器会通知您它是 .error: 'int Super::storage' is protected

最后,最后一个程序将产生以下输出:

storage = 101

评论

4赞 Water 10/26/2016
第一个提到缺少修饰符(如类:超类)会产生私有。这是其他人错过的重要部分,以及详尽的解释。+1
2赞 cp.engr 8/4/2018
矫枉过正的IMO,但我喜欢开头的桌子。
0赞 KcFnMi 12/29/2022
公共继承的受保护成员在派生类中变为私有成员,对吗?表上说它受到保护了吗?
0赞 BugShotGG 1/4/2023
@KcFnMi 不,它说:if a component is declared as protected and its class is inherited as public the resulting access is protected.
0赞 raz 5/23/2023
上面的图表足以让我理解这个概念。谢谢:)
29赞 yuvi 5/26/2016 #15

1)公共继承

一个。在派生类中无法访问 Base 类的私有成员。

b.基类的受保护成员在派生类中仍受保护。

c. 基类的公共成员在派生类中保持公共状态。

因此,其他类可以通过派生类对象使用 Base 类的公共成员。

2) 受保护的继承

一个。在派生类中无法访问 Base 类的私有成员。

b.基类的受保护成员在派生类中仍受保护。

c. 基类的公共成员也成为派生类的受保护成员。

因此,其他类不能通过 Derived class 对象使用 Base class 的公共成员;但它们可用于 Derived 的子类。

3) 私人继承

一个。在派生类中无法访问 Base 类的私有成员。

b.基类的受保护和公共成员成为派生类的私有成员。

因此,其他类不能通过派生类对象访问基类的任何成员,因为它们在派生类中是私有的。因此,即使是 Derived 的子类 类无法访问它们。

8赞 shubhcodegate 9/23/2020 #16

我试过用下面的图片来解释继承。

要点是父类的私有成员永远无法直接从派生/子类访问,但您可以使用父类的成员函数来访问父类的私有成员。 私有变量始终存在于派生类中,但派生类无法访问它。这就像他们的一样,但你不能亲眼看到,但如果你问家长班的人,那么他可以向你描述。Inheritance Mapping cpp

评论

1赞 Soup Endless 3/4/2021
这实际上是我见过的解释继承访问的最好方法。
1赞 glades 6/28/2023 #17

遗产

  • public:无,与基类相同
  • protected:公共 -> protected,否则相同。
  • private(默认):Public & protected -> private(现在一切都是私有的!)

然后,区分类对象从外部和内部的外观非常重要。

从类内部:您可以从 base 访问所有不是 .继承的种类在这里并不重要。private

从外部:您只能访问公共类中的方法和字段。这意味着基础的接口需要是公共的,并且还需要由 derived 公开继承,以便传输访问权限。

怎么样?protected

受保护基本上意味着:不能从外部获得,而是从内部获得。

下面是一些代码来演示它。所有注释都不会编译:

#include <iostream>

struct base {
private:
    int a;
    auto get_a() {return a; }
protected:
    int b;
    auto get_b() {return b; }
public:
    int c;
    auto get_c() {return c; }
};

struct derive_private : private base {
    /* From inside */
    auto print() {
        // a = 3;
        b = 2;
        c = 3;
        // std::cout << get_a() << std::endl;
        std::cout << get_b() << std::endl;
        std::cout << get_c() << std::endl;
    }
};

struct derive_protected : protected base {
    /* From inside */
    auto print() {
        // a = 3;
        b = 14;
        c = 35;
        // std::cout << get_a() << std::endl;
        std::cout << get_b() << std::endl;
        std::cout << get_c() << std::endl;
    }
};

struct derive_public : public base {
    /* From inside */
    auto print() {
        // a = 3;
        b = 14;
        c = 35;
        // std::cout << get_a() << std::endl;
        std::cout << get_b() << std::endl;
        std::cout << get_c() << std::endl;
    }
};

int main() {
    /* From outside */

    derive_private mypriv;
    mypriv.print();
    // mypriv.a = 2;
    // mypriv.b = 5;
    // mypriv.c = 29;
    // std::cout << mypriv.get_a() << std::endl;
    // std::cout << mypriv.get_b() << std::endl;
    // std::cout << mypriv.get_c() << std::endl;

    derive_protected myprot;
    myprot.print();
    // myprot.a = 17;
    // myprot.b = 8;
    // myprot.c = 31;
    // std::cout << myprot.get_a() << std::endl;
    // std::cout << myprot.get_b() << std::endl;
    // std::cout << myprot.get_c() << std::endl;

    derive_public mypub;
    mypub.print();
    // mypub.a = 91;
    // mypub.b = 101;
    mypub.c = 205;
    // std::cout << mypub.get_a() << std::endl;
    // std::cout << mypub.get_b() << std::endl;
    std::cout << mypub.get_c() << std::endl;
}