关于C++:私有、公共和受保护继承的区别

Difference between private, public, and protected inheritance

EDCOX1、0、EDCX1、1、EDCX1和2的遗传差异在C++中有什么不同?我发现的所有问题都要处理特定的案例。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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。这只是访问问题。

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


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

我知道有三个附加器:publicprotectedprivate

让:

1
2
3
4
5
6
7
8
class Base {
    public:
        int publicMember;
    protected:
        int protectedMember;
    private:
        int privateMember;
};
  • 所有了解Base的人都知道Base包含publicMember
  • 只有孩子(和他们的孩子)知道Base包含protectedMember
  • 除了Base以外,没有人知道privateMember

"意识到",我的意思是"承认存在,从而能够进入"。

下一步:

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

  • 如果遗产是public,那么所有知道BaseChild的人也都知道Child继承自Base
  • 如果遗产是以东十一〔1〕号,只有以东十一〔11〕号及其子女知道他们是从以东十一〔3〕号继承的。
  • 如果遗产是以东十一〔2〕号,除以东十一〔11〕号外,没有人知道该遗产。


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

只有类的成员/朋友可以看到私有继承,只有成员/朋友和派生类可以看到受保护的继承。

公共继承

  • 是继承。按钮是一个窗口,在任何需要窗口的地方,也可以传递一个按钮。

    1
    class button : public window { };
  • 受保护的继承

  • 根据实施保护。很少有用。在boost::compressed_pair中用于从空类派生并使用空的基类优化保存内存(下面的示例不使用模板来保持当前状态):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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
    2
    3
    4
    5
    6
    7
    8
    template<typename StorageModel>
    struct string : private StorageModel {
    public:
      void realloc() {
        // uses inherited function
        StorageModel::realloc();
      }
    };
  • 公众成员

  • 骨料

    1
    2
    3
    4
    5
    class pair {
    public:
      First first;
      Second second;
    };
  • 访问器

    1
    2
    3
    4
    class window {
    public:
        int getWidth() const;
    };
  • 受保护成员

  • 为派生类提供增强的访问

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class stack {
    protected:
      vector<element> c;
    };

    class window {
    protected:
      void registerClass(window_descriptor w);
    };
  • 私人成员

  • 保留实施详细信息

    1
    2
    3
    4
    class window {
    private:
      int width;
    };
  • 注意,C样式的强制转换有目的地允许以一种定义的、安全的方式将派生类强制转换为受保护的或私有的基类,并且也允许强制转换到另一个方向。应该不惜一切代价避免这种情况,因为它可以使代码依赖于实现细节——但如果必要,您可以使用这种技术。


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

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

    正如Litb指出的那样,公共继承是传统继承,在大多数编程语言中都可以看到。也就是说,它塑造了一种"即是即是"的关系。私有继承,是C++特有的东西,是一种"以关系来实现"的关系。也就是说,您希望在派生类中使用公共接口,但不希望派生类的用户有权访问该接口。许多人认为,在这种情况下,应该聚合基类,也就是说,为了重用基类的功能,应该将基类作为派生的成员,而不是将基类作为私有的基。


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

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

    enter image description here

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

    if a component is declared as public and its class is inherited as public the resulting access is public.

    一个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
     class Super {
        public:      int p;
        private:     int q;
        protected:   int r;
     };

     class Sub : private Super {};

     class Subsub : public Sub {};

    类中的变量pqr的访问结果为无。

    Another example:

    1
    2
    3
    4
    5
    6
    class Super {
        private:     int x;
        protected:   int y;
        public:      int z;
     };
    class Sub : protected Super {};

    类Sub中的变量yz的结果访问受到保护,而变量x的结果访问则为无。

    A more detailed example:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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;
    }

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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类对象的副本?

    不,没有。

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

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

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

    1
    class Sub : public Super { };

    Don’t be misled: it doesn’t mean that private components of the Super
    class (like the storage variable) will turn into public ones in a
    somewhat magical way. Private components will remain private, public
    will remain public.

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

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

    对。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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类提供了一个新的功能,它做了一件重要的事情:从超级类访问存储变量。

    如果变量声明为private,则不可能。在主函数范围内,变量始终保持隐藏状态,因此,如果编写如下内容:

    1
    object.storage = 0;

    编译器将通知您它是一个error: 'int Super::storage' is protected

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

    1
    storage = 101


    1
    Member in base class : Private   Protected   Public

    继承类型:继承对象为:

    1
    2
    3
    Private            :   Inaccessible   Private     Private  
    Protected          :   Inaccessible   Protected   Protected  
    Public             :   Inaccessible   Protected   Public


    1)公共继承:

    a.基类的私有成员在派生类中不可访问。

    B.基类的受保护成员在派生类中保持受保护。

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

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

    2)受保护的继承:

    a.基类的私有成员在派生类中不可访问。

    B.基类的受保护成员在派生类中保持受保护。

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

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

    3)私有继承:

    a.基类的私有成员在派生类中不可访问。

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

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


    公共继承为IS-A关系建模。用

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

    每个D都是一个B

    私有继承为正在实现的使用关系(或调用的任何关系)建模。用

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

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

    1
    2
    3
    4
    5
    class B {};
    class D {
      private:
        B b_;
    };

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

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


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

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

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

    这基本上代表了其他班级对你和你的家长班级关系的了解。


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

    1
    2
    3
    4
    5
    6
    class MyClass {
        private:
            int myPrivateMember;    // lol
        protected:
            int myProtectedMember;
    };

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


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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…我想一张小桌子值一千个字:)


    总结:

    • private:除了在课堂上,没有人能看到它。
    • 受保护:私有+派生类可以看到它
    • 公众:全世界都能看到

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


    私人:

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

    公众:

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

    受保护的:

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

    简而言之:

    私人:基地

    受保护:基础+衍生

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


    我找到了一个简单的答案,所以想把它贴出来,以备将来参考。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    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
    }

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