1

我对 C++ 继承中的访问级别以及更普遍地应该如何设计 C++ 类有一些困惑。

class Car
{
public:

private:
    string brandName;
}

class Sedan: public Car
{
public:
    // this function needs to know the Sedan's brandName
    void someFun()
    {
        brandName = "sss"; // error!!!    
    }
private:
}

在上面的例子中,作为基类的 Car 有一个私有成员“brandName”,作为 Car 的派生类的 Sedan 继承了该成员“brandName”。正如 Effective C++ 中所解释的,公共继承建立了“is-a”关系,这意味着 Sedan 是 Car,现在我可以说 Sedan 有一个“brandName”吗?如果答案是肯定的,为什么 Sedan 不能在 someFun 中访问它自己的属性?

我知道解决方案是将brandName的访问级别更改为受保护,我的困惑是什么特性或条件使变量成员可以访问哪个访问级别,换句话说,我如何决定给定成员应该附加到哪个访问级别?

如果您推荐任何有关此主题的书籍或文章,我也很乐意。

4

6 回答 6

3

现在我可以说轿车有一个“品牌名称”吗?

不,也没有Car。是一个详细的brandName实现,私有的,对其他任何人都隐藏,并且不会对类的公共接口做出贡献,这就是Car. 因此,虽然从技术上讲,汽车和轿车中的某处都有品牌名称,但从纯粹的面向对象的角度来看,这并不重要。类似于仅通过公共继承而非私有继承表示的“Is-A”-关系,OO 观点中的“Has-A”-关系仅在组合或关联可公开访问/可见时才存在(主要是通过getter/setter)。

juanchopanza 的回答和对它的评论让我在网上偷偷摸摸,试图找到有关 Is-A 和 Has-An 关系的资源。转录似乎表明这两个术语并非源自 OO 思想。事实上,OO 文献似乎引用了 Liskov 替换原则,而不是使用“Is-A”关系。它强调封装和不透明对象,因此它主要关注类的公共接口以及它们的行为如何影响关联的对象。“Has-A”关系可以是关联,也可以是聚合。在这种情况下,由于brandName不是外部的关联对象(在 C++ 中,我们会用指针或引用成员来表达这一事实),它是一个聚合-“Has-A”,Car因此对 OO 不感兴趣(并且上述答案在语义上是有效的)。

我知道解决方案是将brandName 的访问级别更改为受保护

不!brandName的访问级别必须保持私有,这样才能Car完全控制发生的事情。如果您想在派生类中访问它,请提供受保护的 getter 和/或 setter。

如果您推荐任何有关此主题的书籍或文章,我也很乐意。

任何有关面向对象设计的书都应该这样做。如果您阅读其中的两个或更多,您将很好地了解该做什么和不该做什么。

于 2013-08-20T06:26:36.297 回答
2

我可以说轿车有一个“品牌”吗?

是的,它肯定有一个string名为的对象Car::brandName(这是否构成 OOP“具有”关系对我来说并不完全清楚,但可能不是:见评论)

如果答案是肯定的,为什么 Sedan 不能在 someFun 中访问它自己的属性?

很简单,因为brandNameprivate。这些是语言的规则。如果要让派生类访问非公共数据成员,可以将其设为protected.

如果您推荐任何有关此主题的书籍或文章,我也很乐意。

这里有一个书单。也许“C++ 编程语言”可以很好地解释这个特定方面。

于 2013-08-20T06:23:14.873 回答
1

通过将其设为私有,您可以从外部世界隐藏品牌名称。使其受到保护,以便孩子们可以继承它。要了解有关说明符的继承规则,请转到此处

于 2013-08-20T06:32:55.527 回答
1

我对 C++ 继承中的访问级别以及更普遍地应该如何设计 C++ 类有一些困惑。

C++ 具有三个可在继承时使用的访问级别说明符:

  • 私有的、受保护的和公共的

派生类:公共基础

称为公共继承,代表IsA关系。公开继承时,基的公共成员在 Derived 中保持公开,受保护的成员在 Derived 中保持保护,私有成员是私有的(不能从 Derived 访问)。

class Derived : protected Base 称为受保护的继承。这种继承很少见,如果您不想在作为派生(或通过派生的接口)访问时暴露 Base 的公共部分,则可以使用这种继承。在这种继承中,基类中的公共成员在派生中受到保护。

另请参阅 C++ 常见问题解答

派生类:私有基础

称为私有继承。这种继承可以用来模拟遏制(Herb Sutter - 继承的使用和滥用)。这里 Base 中的公共成员在通过派生接口访问时变为私有。

还可以注意到,受保护和私有继承并不代表经典的 IsA 关系。以下多态行为仅在公开继承时才有可能:

Derived d;
Base* b = &d;

但是,对于私有继承,多态行为在第一个派生类中是可能的(但在后续类中则不行)。

struct Base{};
struct Derived : private Base
{
  Derived()
  {
    //IsA holds within member functions of Derived, but not
    // outside
    Base* b = this; 
  }
};
struct Derived2 : public Derived
{
  Derived2()
  {
    Base* b = this; //Fails to compile...inaccessible base
  }
};
int main()
{
  Derived d;
  Base* b = &d; //Fails to compile
}

对于受保护的继承,多态行为在所有后续派生类中都是可能的(因此上面的 Derived2 的构造函数中的代码将编译),但在类成员函数之外。

Herb Sutter 评论了在Uses and abuses中使用非公共继承的原因。

最后,关于您上面的示例的评论:通常 Car 将是抽象的(一个接口,仅由纯虚函数组成),因此它不包含任何数据成员,而是将其开放给实现。这是我在某处Sutter - Exceptional C++听说过的关于继承的指南:

公开继承,以便被多态使用基类的代码重用。

您的示例将/可能变为:

struct Car
{
  //Note: const used as brandName should not modify logical state.
  virtual const std::string& brandName() const = 0;

  //...virtual ~Car(), amongst others, depending
  // on whether you intend to be deleted via this interface...
};

// Note: public inheritance implied by struct
struct CarFromFile: /*public*/ Car
{
  CarFromFile( std::ifstream& file )
  : brandName_( strFrom( file ) ),
    manufacturer_( strFrom( file )
  {
  }
  virtual const std::string& brandName() const{ return brandName_; }
  private:
    std::string strFrom( std::ifstream& file )
    {
      std::string value;
      if( file >> value ) { return value; }
      throw std::runtime_error( "Invalid file format!" );
    }
    std::string brandName_;
    std::string manufacturer_;
    //etc...
};

您使访问器抽象的事实允许从派生的实施者的角度自由,并定义基客户端所需的服务,而与实际数据的外观无关。

于 2013-08-20T07:58:26.770 回答
1

公共成员可以免费使用。

私有成员仅供当前类使用,其他人不得使用。它主要用于类的内部成员为自己的目的使用和保存或提供访问它们的方法。

继承类可以访问受保护的成员,正如您的示例所示 - brandName 用于继承类,因此它应该受到保护。

于 2013-08-20T06:27:19.857 回答
0

public的分类旨在允许将公开可用的接口与内部实现细节分开。因此,不是不可访问的东西,迫使用户使用提供的接口。protectedprivatepublicclasspublic

如果某物是private,那么即使继承将使成员成为派生的一部分,class派生也不允许访问它。如果希望允许class对接口和不属于public接口一部分的成员进行派生访问,那么这正是使用protected.

于 2013-08-20T06:28:11.533 回答