C++子类化访问修饰符?

21

我是一名 C++ 新手,但已经有多年使用面向对象语言如 C/C#/Objective-C 的经验。现在,我正在学习 C++。

我看到了这段 C++ 代码:

    class World : public State
    {
    };

看起来类 World 公开继承了类 State。 公开子类化?这很难理解。

这个特性的概念是什么? 以及何时使用或需要此功能?


你是不是想写 class World: private State {}?实际上你写的继承是公有的。 - Steve Jessop
你是不是想说 class World: private State - detunized
1
“公共”继承是“正常”的继承方式,例如在C#中(只是举个你知道的语言),问题出在哪里?我发现更难理解“私有”继承。 - Matteo Italia
1
@Eonil:嗯,好的。:) 顺便说一下,你可以省略继承访问修饰符,但是对于类来说,默认值为“private”,在99%的情况下是无用的(对于结构体而言,默认值为“public”)。 - Matteo Italia
@Eonil:我不知道,在Java中默认的访问修饰符是包保护,这只适用于成员而不适用于继承。至少C++在封装方面犯了错误;-) - Steve Jessop
显示剩余4条评论
4个回答

24

在那里需要使用public关键字的原因是,对于使用class关键字定义的类,所有成员(数据成员、成员函数和基类)的默认访问修饰符都是private。因此,

class World : State {};

与以下代码等效:

class World : private State {};

这可能不是您想要的 - 这意味着基类仅在World类内部可访问。外部人员根本不知道存在继承关系。

对于使用关键字struct定义的类,访问修饰符的默认值为public,因此您可以编写以下代码:

struct World : State {};

想要得到一个既外表看起来,又行为类似于其他支持继承的语言的东西。但是struct关键字及其定义类的事实,仅仅是为了与C兼容而存在。你不会在许多C++样式指南中找到推荐使用它的建议,只是为了获得默认的public可访问性 -普遍情况下只用于POD类或者可能仅用于没有成员函数的类。

至于为什么C ++首先有私有继承:对于大多数目的,私有继承是一种组合形式。普通的组合:

class World {
    State state;
  public:
    void foo() {
        state.bar();
        state.baz();
        and so on
    }
};

也就是说,类World知道它是使用状态实现的,而外部世界不知道World是如何实现的。

与之相对。

class World : private State {
  public:
    void foo() {
        bar();
        baz();
        and so on
    }
};

换句话说,类“World”知道它是通过成为“State”实现的,并且外部世界不知道它是如何实现的。但是,您可以通过例如在World的公共部分中放置using State::bar;来有选择性地暴露State接口的某些部分。效果就好像您费力地编写了一个函数(或多个重载),每个函数都将代理到State上的同一个函数。

除了避免输入之外,私有继承的另一个常见用途是当类“State”为空时,即没有数据成员时。然后,如果它是“World”的成员,则必须占用一些空间(尽管根据对象布局,这可能是否则将只是填充的空间,因此它不一定会增加“World”的大小),但如果它是基类,那么一个叫做“空基类优化”的东西就会发挥作用,它可以是零大小的。如果您要创建许多对象,则可能会很重要。私有继承启用了优化,但外部世界不会推断出“is-a”关系,因为它看不到继承。

这是一个相当微妙的区别——如果有疑问,只需使用显式组合即可。为了节省输入而引入继承是很好的,直到它有一些意外后果。


1
谢谢。接受了因为最后一段 :) - eonil

7
如果…的话
class World: private State
{
};

私有继承意味着所有Statepublicprotected成员将被World继承并变为private。这将State封装在World中。任何从World继承的类都无法访问State的任何特性。

3
你认为它是私有的吗?它明确写着公共的,这意味着它是公共子类。
除此之外,私有和受保护继承所做的与公共继承相同,不同之处在于所有成员变量和函数都继承了至少私有或受保护的可访问性。例如,如果State有一个公共成员函数'foo()',那么在World中它将是私有的。
这在实践中很少使用,但确实有其用途。我见过的最常见用途是通过继承进行组合。即你想要一个“拥有”关系而不是通常使用公共继承得到的“是”关系。通过私有继承该类,您可以获得所有变量和方法,但不会将它们暴露给外部世界。
使用私有继承进行组合的一个优点来自空基类优化(EBCO)。使用普通组合,拥有一个空类的成员对象仍然会使用至少1个字节,因为所有变量必须具有唯一地址。如果您私有继承要组合的对象,则不适用该规则,您不会遭受内存损失。
例如:
class Empty { };

class Foo
{
    int foo;
    Empty e;
};

class Bar : private Empty
{
    int foo;
};

这里,sizeof(Foo)可能是5,但是由于空的基类,sizeof(Bar)将会是4。


与公共继承相同,除了...。不同之处不仅仅在于此。此外,外部人员无法将World*静态转换为State* - Steve Jessop
如果您的实现中int是4字节对齐的话,那么sizeof(Foo)可能会是5或者更糟糕的8。 - Steve Jessop

1

在祖先类名前的public/protected/private关键字表示从祖先继承的成员的可见性。使用private继承,子类只继承祖先的实现,而不继承接口。

class A {
public:
  void foo();
};

class B : private A {
public:
  void bar();
};

void B::bar()
{
  foo();  // can access foo()
}

B b;
b.foo(); // forbidden
b.bar(); // allowed

一般来说,你应该使用公共继承,因为继承不应仅用于实现重用(这是私有继承所做的)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接