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

952 c++ inheritance encapsulation c++-faq access-specifier

是什么区别public,private以及protected用C++继承?我在SO上发现的所有问题都涉及具体案例.

Kir*_*sky 1392

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
};
Run Code Online (Sandbox Code Playgroud)

重要说明:B,C和D类都包含变量x,y和z.这只是访问问题.

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

  • 什么Anzurio [写](http://stackoverflow.com/a/860353/2809095)只在下面与你的答案一起点击.Плус1. (31认同)
  • 值得注意的是,“私有”是类的默认设置。 (3认同)
  • 我对它如何运作的理解是如此之快!非常感谢您的澄清. (2认同)

Anz*_*rio 1023

要回答这个问题,我想先用自己的话来描述成员的访问者.如果您已经知道这一点,请跳至标题"next:".

有三个访问器,我所知道的:public,protectedprivate.

让:

class Base {
    public:
        int publicMember;
    protected:
        int protectedMember;
    private:
        int privateMember;
};
Run Code Online (Sandbox Code Playgroud)
  • 知道的一切Base也意识到Base包含publicMember.
  • 只有孩子(和他们的孩子)知道Base包含protectedMember.
  • 没人Base知道privateMember.

通过"意识到",我的意思是"承认存在,从而能够访问".

下一个:

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

  • 如果继承是public,所有知道Base并且Child也知道Child继承自的东西Base.
  • 如果继承protected只是Child和它的子进程知道它们是继承的Base.
  • 如果继承是private,除了Child知道继承之外没有人.

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

Joh*_*itb 110

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

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

公共继承

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

    class button : public window { };
    
    Run Code Online (Sandbox Code Playgroud)

受保护的继承

  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!
      }
    };
    
    Run Code Online (Sandbox Code Playgroud)

私人继承

  1. 实现合条件方面.基类的用法仅用于实现派生类.对traits有用,如果size很重要(只包含函数的空特征将使用空基类优化).但是,通常遏制是更好的解决方案.字符串的大小是至关重要的,因此这是一个常见的用法

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

公共成员

  1. 骨料

    class pair {
    public:
      First first;
      Second second;
    };
    
    Run Code Online (Sandbox Code Playgroud)
  2. 访问器

    class window {
    public:
        int getWidth() const;
    };
    
    Run Code Online (Sandbox Code Playgroud)

受保护的成员

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

    class stack {
    protected:
      vector<element> c;
    };
    
    class window {
    protected:
      void registerClass(window_descriptor w);
    };
    
    Run Code Online (Sandbox Code Playgroud)

私人会员

  1. 保持实施细节

    class window {
    private:
      int width;
    };
    
    Run Code Online (Sandbox Code Playgroud)

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

  • 我认为斯科特迈尔斯(尽管我喜欢他的东西)对于一般的困惑有很多答案.我现在认为他对IS-A和IS-IMPLEMENTED-TERMS-OF的类比足以应对正在发生的事情. (7认同)

Dou*_* T. 62

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

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

正如litb指出的那样,公共继承是传统的继承,你会在大多数编程语言中看到.那就是模仿"IS-A"关系.私有继承,是C++特有的AFAIK,是一种"实现的术语"关系.也就是说,您希望在派生类中使用公共接口,但不希望派生类的用户有权访问该接口.许多人认为,在这种情况下,您应该聚合基类,而不是将基类作为私有基础,在派生成员中生成以重用基类的功能.

  • 更好地说"公众:每个人都会看到遗产".protected:继承只能由派生类和朋友看到","private:继承只能由类本身和朋友看到."这与你的措辞不同,因为不仅成员可以是隐形的,而且IS-A关系可以是不可见的. (13认同)
  • 有一次我使用私有继承就是做了Doug T描述的内容,即"你想在派生类中使用公共接口,但不希望派生类的用户有权访问该接口".我基本上用它来封锁旧接口并通过派生类暴露另一个接口. (4认同)

Bug*_*tGG 60

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

此表收集组件声明和继承模型的所有可能组合,在完全定义子类时显示对组件的结果访问.

在此输入图像描述

上面的表格按以下方式解释(请看第一行):

如果组件声明public且其类继承public,则生成的访问权限公共的.

一个例子:

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

 class Sub : private Super {};

 class Subsub : public Sub {};
Run Code Online (Sandbox Code Playgroud)

变量产生的访问p,q,rSubsub没有.

另一个例子:

class Super {
    private:     int x;
    protected:   int y;
    public:      int z;
 };
class Sub : protected Super {};
Run Code Online (Sandbox Code Playgroud)

变量产生的访问y,z在课堂上保护和变量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;
}
Run Code Online (Sandbox Code Playgroud)

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

class Sub : Super { };

int main(void) {
    Sub object;

    object.put(100);
    object.put(object.get());
    cout << object.get() << endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

定义的类名为Sub,它是已命名的类的子类,Super或者Sub该类是从Super类派生的.该Sub课程介绍既不是新的变量,也没有新的功能.是否意味着Sub类的任何对象在类Super实际上是Super类对象的副本之后继承了所有特征?

.它没有.

如果我们编译以下代码,我们将得到的只是编译错误,说明put并且get方法不可访问.为什么?

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

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

class Sub : public Super { };
Run Code Online (Sandbox Code Playgroud)

不要被误导:这并不意味着Super类的私有组件(如存储变量)将以某种神奇的方式变成公共组件.私人组件将保持私密,公众 将继续公开.

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

这是一个非常严重的限制.有没有解决方法?

是的.

第三个访问级别称为受保护.关键字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;
}
Run Code Online (Sandbox Code Playgroud)

正如您在示例代码中看到的那样,我们为Sub类提供了一项新功能,它执行了一项重要操作:它从Super类访问存储变量.

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

object.storage = 0;
Run Code Online (Sandbox Code Playgroud)

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

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

storage = 101
Run Code Online (Sandbox Code Playgroud)

  • 首先提到缺少修饰符(如Class:SuperClass)会产生私有.这是其他人遗漏的重要部分,以及详尽的解释.+1 (3认同)
  • IMO 矫枉过正,但我​​喜欢一开始的桌子。 (2认同)

kin*_*uk4 36

Member in base class : Private   Protected   Public   
Run Code Online (Sandbox Code Playgroud)

继承类型 :              对象继承为:

Private            :   Inaccessible   Private     Private   
Protected          :   Inaccessible   Protected   Protected  
Public             :   Inaccessible   Protected   Public
Run Code Online (Sandbox Code Playgroud)

  • 这种误导.基类的私有成员与普通私有类成员的行为完全不同 - 根本不能从派生类访问它们.我认为你的三个专栏"私人"应该是一个"无法访问"栏目.请参阅Kirill V. Lyadvinsky对此问题的回答. (23认同)

yuv*_*uvi 26

1)公共继承:

一个.Derived类中无法访问Base类的私有成员.

湾 Base类的受保护成员在Derived类中保持受保护.

C.Base类的公共成员在Derived类中保持公共状态.

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

2)受保护的继承:

一个.Derived类中无法访问Base类的私有成员.

湾 Base类的受保护成员在Derived类中保持受保护.

C.Base类的公共成员也成为Derived类的受保护成员.

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

3)私人继承:

一个.Derived类中无法访问Base类的私有成员.

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

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


sbi*_*sbi 20

公共继承模拟IS-A关系.同

class B {};
class D : public B {};
Run Code Online (Sandbox Code Playgroud)

每一个D 都是 B.

私有继承模拟IS-IMPLEMENTED-USING关系(或任何被称为的关系).同

class B {};
class D : private B {};
Run Code Online (Sandbox Code Playgroud)

一个D不是一个B,但每一个D使用它B的实施.可以通过使用包含来消除私有继承:

class B {};
class D {
  private: 
    B b_;
};
Run Code Online (Sandbox Code Playgroud)

D也可以使用B,在这种情况下使用它来实现b_.包含是类型之间不那么紧密的耦合而不是继承,所以通常它应该是首选.有时使用包含而不是私有继承不如私有继承方便.这通常是懒惰的蹩脚借口.

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


Ark*_*nez 11

如果你从另一个类公开继承,每个人都知道你是继承的,任何人都可以通过基类指针多态地使用它.

如果你继承受保护的只有你的子类可以多态地使用你.

如果你私下继承只有你自己才能执行父类方法.

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


And*_*yes 9

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

class MyClass {
    private:
        int myPrivateMember;    // lol
    protected:
        int myProtectedMember;
};
Run Code Online (Sandbox Code Playgroud)

从您的扩展程序到此类,引用this.myPrivateMember将不起作用.但是,this.myProtectedMember会的.该值仍然是封装的,因此如果我们调用了这个类的实例化myObj,那么myObj.myProtectedMember它将无法工作,因此它在功能上类似于私有数据成员.


Eni*_*say 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
Run Code Online (Sandbox Code Playgroud)

基于这个例子的java ...我认为一个值得千言万语的小桌子:)


Roe*_*ler 7

摘要:

  • 私人:除了课堂之外,没有人能看到它
  • 受保护:私有+派生类可以看到它
  • 公众:世界可以看到它

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


小智 6

私人的:

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

上市:

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

保护:

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


简而言之:

私人:基地

protected:base + derived

public:base + derived +任何其他成员


shu*_*ate 5

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

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