私有,公共和受保护的继承之间的区别


Answers:


1064

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

有三个访问器,我所知道的:publicprotectedprivate

让:

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
  • 如果protected仅是继承,则Child其及其子级将意识到它们是从继承的Base
  • 如果继承为private,则没有人Child知道继承。

182
我想补充一点,C ++中的可见性基于类而不是对象,这意味着同一类的对象可以不受限制地访问彼此的私有字段。
陈哲

48
如果您很难理解这一点,请阅读Kirill V. Lyadvinsky的答案,然后回来阅读。
Vivandiere

6
这只是另一种情况,它说明了在很大程度上继承SomeBase是如何像硬编码方式组成type的匿名成员的SomeBase。像任何其他成员一样,它具有访问说明符,该说明符对外部访问施加相同的控制。
underscore_d

1
@ZheChen如果我有班上的汤姆和杰瑞的对象拥有私有年龄的人您如何使用汤姆来访问(并修改?)杰瑞的年龄?
gen

2
您能否说明“了解'继承'”的意思?我可以理解“我可以访问它,但我不能访问它”,但是当有人说“我知道A继承自B”时,我不明白我在做什么?我在检查继承吗?
neilxdims

1458
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。这只是访问的问题。

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


35
Anzurio 撰写的内容仅在下面直接与您的答案结合在一起单击。Плус1.
Iwillnotexist Idonotexist

2
我对这是如何工作的了解太差了!非常感谢您的澄清。
tjwrona1992

我花了一些时间来理解这一点。但是现在很明显。谢谢!
灿金

115

限制继承的可见性将使代码无法看到某个类继承了另一个类:从派生到基础的隐式转换将不起作用,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
我认为,斯科特·迈尔斯(Scott Myers)(就我所喜欢的东西而言)对于普遍的困惑有很多答案。我现在认为他对IS-A和IS-IMPLEMENTED-IN-TERMS-OF的类比足以说明正在发生的事情。
DangerMouse

65

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

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

在此处输入图片说明

上表通过以下方式进行解释(请看第一行):

如果一个组件被声明public并且其类被继承public,则结果访问public

一个例子:

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

 class Sub : private Super {};

 class Subsub : public Sub {};

变量产生的访问pqrSubsub没有

另一个例子:

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

变量产生的访问yz在课堂上保护和变量x没有

更详细的示例:

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的类是Sub类的子类,Super或者Sub该类是从Super该类派生的。该Sub课程介绍既不是新的变量,也没有新的功能。这是否意味着Sub该类的任何对象Super实际上是Super该类的对象的副本之后,便继承了所有特征?

没有。没有。

如果我们编译下面的代码,除了编译错误说putget方法不可访问外,我们什么都不会得到。为什么?

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

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

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

4
第一个提到缺少修饰符的类(如Class:SuperClass)产生私有的。这是其他人所缺少的重要内容,同时还有详尽的解释。+1

2
过度使用IMO,但一开始我喜欢这张桌子。
cp.engr

63

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

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

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


13
最好说“公开:继承将被所有人看到”。保护:继承只能由派生的类和朋友看到”,“私有:继承只能由类本身和朋友看到”。这与您的措辞不同,因为不仅成员可以是不可见的,而且成员也可以IS-A关系可能是不可见的
Johannes Schaub-litb

4
我曾经使用私有继承做的正是Doug T所描述的,即“您想在派生类中使用公共接口,但不希望派生类的用户能够访问该接口”。我基本上用它来密封旧接口,并通过派生类公开另一个接口。
Rich

36
Member in base class : Private   Protected   Public   

继承类型 :              对象继承为

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

23
这种误导。基类的私有成员的行为与普通的私有类成员非常不同-派生类根本无法访问它们。我认为您的三个“私人”列应为“无法访问”列。请参阅Kirill V. Lyadvinsky对这个问题的回答。
山姆·考夫曼

27

1)公共继承

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

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

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

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

2)受保护的继承

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

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

C。基类的公共成员也将成为派生类的受保护成员。

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

3)私有继承

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

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

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


20

公共继承为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_。与继承相比,包含是类型之间的紧密耦合,因此通常应首选。有时使用包容代替私有继承不如私有继承方便。通常这是懒惰的la脚借口。

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


有人说关系。就像用椅子当锤子一样。椅子:受保护的锤子
user4951 2013年

什么时候使用包容而不是私有继承不如私有继承方便?您能用一个例子解释一下吗?
破坏者

@Pravasi:如果是D私自派生的D,它可以覆盖的虚函数B。(例如,如果B是观察者接口,则D可以实现它并将其传递this给需要一个接口的函数,而无需每个人都可以D用作观察者。)而且,D可以B通过执行使其在其接口中有选择地成为可用成员using B::member。当B成为成员时,两者在语法上都不方便实现。
2015年

@sbi:旧的但是...对于CRTP和/或虚拟机来说,包含是不可行的(正如您在注释中正确描述的那样-但这意味着如果B具有抽象方法并且您不能将其建模为包含,不允许触摸)。protected我发现virtualprotectedstruct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
继承对基

11

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

如果您保护性地继承您的子类,则可以多态使用您。

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

这基本上象征着其余班级对您与父母班级关系的了解


9

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

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

从您的扩展到此类,引用this.myPrivateMember将不起作用。但是,this.myProtectedMember会。该值仍被封装,因此,如果我们有一个名为的此类的实例化myObjmyObj.myProtectedMember则将无法使用,因此其功能类似于私有数据成员。


8
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范例,我觉得一张价值一千字的小桌子:)


Java只有公共继承
Zelldon

这不是要谈论Java的话题,但不是,您错了 ...请按照上面我的答案中的链接获取详细信息
Enissay 2015年

您提到了Java,所以它是主题。您的示例处理了在jaca中使用的说明符。问题是关于Java中不存在的继承说明符,这有所作为。如果超类中的字段是公共的,而继承是私有的,则该字段仅在子类内部是必需的。在外面,没有迹象表明子类是否扩展了超类。但是您的表仅说明字段和方法的说明符。
Zelldon

7

摘要:

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

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


6

私人的:

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

上市:

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

受保护的:

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


简而言之:

私人的:基地

保护:基本+派生

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


5

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

它来自链接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
}

3

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.