【问题标题】:Difference between private, public, and protected inheritance私有继承、公有继承和受保护继承之间的区别
【发布时间】:2010-10-26 00:15:37
【问题描述】:

C++中public、private和protected继承有什么区别?

我在 SO 上找到的所有问题都涉及特定案例。

【问题讨论】:

    标签: c++ inheritance encapsulation access-specifier c++-faq


    【解决方案1】:
    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。这只是访问权限的问题。

    关于受保护和私有继承的使用,您可以阅读here。

    【讨论】:

    • 什么 Anzurio wrote 只在下面与您的答案一起点击。 Плус 1.
    • 我对它的工作原理的理解太差了!非常感谢您的澄清。
    • 我花了一些时间才明白这一点。但现在很清楚了。谢谢!
    • 关于“私人”是类的默认设置的注意事项。
    • 奇妙的解释。到目前为止,一切都清楚了。
    【解决方案2】:

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

    我知道有三个访问器:public、protected 和 private。

    让:

    class Base {
        public:
            int publicMember;
        protected:
            int protectedMember;
        private:
            int privateMember;
    };
    
    • 所有知道Base 的东西也都知道Base 包含publicMember。
    • 只有孩子(和他们的孩子)知道Base 包含protectedMember。
    • 除了Base,没有人知道privateMember。

    “知道”是指“承认存在,因此能够访问”。

    下一个:

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

    • 如果继承是public,所有知道Base和Child的东西也都知道Child继承自Base。
    • 如果继承是protected,则只有Child 及其子级知道它们是从Base 继承的。
    • 如果继承是private,则除了Child 之外没有其他人知道继承。

    【讨论】:

    • 我想补充几句,C++中的可见性是基于类而不是基于对象的,这意味着同一个类的对象可以不受限制地访问彼此的私有字段。
    • 如果您很难理解这一点,请阅读 Kirill V. Lyadvinsky 的回答,然后再回来阅读。
    • 这只是另一个例子,它说明了在大多数情况下,从SomeBase 继承就像是一种硬编码的方式来组成SomeBase 类型的匿名成员。与任何其他成员一样,它有一个访问说明符,它对外部访问施加相同的控制。
    • @ZheChen 如果我有对象 Tom 和 Jerry 的类 Person 具有私有字段 age 你如何使用 Tom 访问(和修改?)Jerry 的年龄?
    • 您能说明一下您所说的“意识到‘继承’”是什么意思吗?我可以理解“我可以访问这个我不能访问那个”,但是当有人说“我知道 A 从 B 继承”时我不明白我在这里做什么我在检查继承吗?
    【解决方案3】:

    限制继承的可见性将使代码无法看到某个类继承了另一个类:从派生到基的隐式转换不起作用,从基到派生的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. 按条款实施。基类的使用仅用于实现派生类。对特征有用,如果大小很重要(只包含函数的空特征将利用空基类优化)。不过,containment 通常是更好的解决方案。字符串的大小很关键,所以这里经常使用它

      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 风格的强制转换有意允许以一种已定义且安全的方式将派生类强制转换为受保护或私有的基类,并且也可以强制强制转换为另一个方向。应该不惜一切代价避免这种情况,因为它会使代码依赖于实现细节——但如果有必要,您可以利用这种技术。

    【讨论】:

    • 我认为 Scott Myers(尽管我很喜欢他的东西)对于普遍的困惑有很多要回答的问题。我现在认为他对 IS-A 和 IS-IMPLEMENTED-IN-TERMS-OF 的类比足以说明正在发生的事情。
    【解决方案4】:

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

    该表收集了组件声明和继承模型的所有可能组合,展示了子类完全定义后对组件的访问。

    上表解释如下(看第一行):

    如果一个组件声明为public并且其类继承为public,则结果访问是公开的。

    一个例子:

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

    Subsub 类中变量 p、q、r 的最终访问权限为 none。

    另一个例子:

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

    对 Sub 类中的变量 y、z 的最终访问是 protected 的,而对于变量 x 的访问是 none。

    更详细的例子:

    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 的类是名为 Super 的类的子类,或者 Sub 类派生自 Super 类。 Sub 类既没有引入新变量,也没有引入新函数。这是否意味着Sub 类的任何对象都继承了Super 类实际上是Super 类对象的副本之后的所有特征?

    否。没有。

    如果我们编译以下代码,我们只会得到编译错误,指出 put 和 get 方法无法访问。为什么?

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

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

    class Sub : public Super { };
    

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

    Sub 类的对象可以做“几乎”与从Super 类创建的兄弟姐妹一样的事情。 “几乎”,因为作为子类的事实也意味着 类无法访问超类的私有组件。我们不能编写能够直接操作存储变量的Sub 类的成员函数。

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

    是的。

    第三个访问级别称为protected。关键字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;
    }
    

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

    如果变量被声明为私有是不可能的。 在主函数范围内,变量仍然是隐藏的,所以如果你写这样的东西:

    object.storage = 0;
    

    编译器会通知你这是一个error: 'int Super::storage' is protected。

    最后,最后一个程序会产生如下输出:

    storage = 101
    

    【讨论】:

    • 第一个提到缺少修饰符(如在 Class : SuperClass 中)产生私有。这是其他人错过的重要部分,以及详尽的解释。 +1
    • Overkill IMO,但我喜欢开头的表格。
    【解决方案5】:

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

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

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

    【讨论】:

    • 最好说“公开:每个人都可以看到继承”。 protected:继承只会被派生类和朋友看到”,“private:继承只会被类本身和朋友看到”。这与您的措辞不同,因为不仅成员可以不可见,而且还可以IS-A 关系可以是不可见的。
    • 我使用私有继承的一次是做 Doug T 所描述的,即“你想在派生类中使用公共接口,但不希望派生类的用户拥有访问该界面”。我基本上是用它来密封旧接口并通过派生类暴露另一个接口。
    【解决方案6】:
    Member in base class : Private   Protected   Public   
    

    继承类型:            对象继承为:

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

    【讨论】:

    • 这种误导。基类的私有成员的行为与普通的私有类成员完全不同——它们根本无法从派生类访问。我认为您的三个“私人”栏应该是“无法访问”栏。请参阅 Kirill V. Lyadvinsky 对此问题的回答。
    【解决方案7】:

    1) 公共继承:

    一个。 Base 类的私有成员在 Derived 类中不可访问。

    b. Base 类的受保护成员在 Derived 类中仍然受到保护。

    c。 Base 类的公共成员在 Derived 类中仍然是公共的。

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

    2) 受保护的继承:

    一个。 Base 类的私有成员在 Derived 类中不可访问。

    b. Base 类的受保护成员在 Derived 类中仍然受到保护。

    c。 Base 类的公共成员也成为 Derived 类的受保护成员。

    所以,其他类不能通过派生类对象使用基类的公共成员;但它们可用于 Derived 的子类。

    3) 私有继承:

    一个。 Base 类的私有成员在 Derived 类中不可访问。

    b. Base 类的受保护和公共成员成为 Derived 类的私有成员。

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

    【讨论】:

      【解决方案8】:

      公共继承对 IS-A 关系建模。与

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

      每个D 都是一个 B。

      私有继承对 IS-IMPLEMENTED-USING 关系(或任何所谓的)建模。与

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

      D不是B,但每个D 在其实现中都使用它的B。私有继承总是可以通过使用容器来消除:

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

      这个D 也可以使用B 来实现,在本例中使用它的b_。与继承相比,包含在类型之间的耦合不那么紧密,因此通常应该首选它。有时使用包含而不是私有继承不如私有继承方便。这通常是懒惰的一个蹩脚的借口。

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

      【讨论】:

      • 有人说是一种关系。就像把椅子当锤子一样。这里的椅子:保护锤
      • 何时使用包含而不是私有继承不如私有继承方便?请你用一个例子来解释一下吗?
      • @Pravasi:如果D 私有派生自D,它可以覆盖B 的虚函数。 (例如,如果B 是一个观察者接口,那么D 可以实现它并将this 传递给需要一个接口的函数,而每个人都不能使用D 作为观察者。)另外,@ 987654341@ 可以通过执行using B::member 选择性地使B 的成员在其界面中可用。当B 是成员时,两者在语法上都不方便实现。
      • @sbi:旧的但是......在 CRTP 和/或虚拟的情况下,遏制是不可行的(正如您在评论中正确描述的那样 - 但这意味着它不能被建模为如果 B 有抽象方法并且不允许你触摸它,则包含)。 protected继承我发现virtual基类和protectedctor:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff&amp; stuff) : CommonStuff(&amp;stuff) {} };
      【解决方案9】:

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

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

      如果你私有继承,只有你自己能够执行父类方法。

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

      【讨论】:

        【解决方案10】:

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

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

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

        【讨论】:

          【解决方案11】:
          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
          

          基于this java 的示例...我认为一张小表值得一千字:)

          【讨论】:

          • Java 只有公共继承
          • 这不是关于 java 的话题,而是 不,你错了...点击上面我的答案中的链接了解详细信息
          • 你提到了java,所以它是主题。您的示例处理了在 jaca 中使用的说明符。问题是关于 Java 中不存在的继承说明符并有所作为。如果超类中的字段是公共的并且继承是私有的,则该字段只能在子类内部访问。外部没有迹象表明子类是否扩展了超类。但是您的表格只解释了字段和方法的说明符。
          【解决方案12】:

          总结:

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

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

          【讨论】:

            【解决方案13】:

            私人:

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

            公开:

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

            受保护:

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


            简而言之:

            私有:基础

            受保护:基础 + 派生

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

            【讨论】:

              【解决方案14】:

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

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

              【讨论】:

              • 这实际上是我见过的解释继承访问的最佳方式。
              【解决方案15】:

              我找到了一个简单的答案,因此也想将其发布以供我将来参考。

              来自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
              }
              

              【讨论】:

                【解决方案16】:

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2015-07-14
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-02-15
                  • 2019-08-24
                  相关资源
                  最近更新 更多