【问题标题】:Should superclasses have constructors?超类应该有构造函数吗?
【发布时间】:2018-12-11 06:41:15
【问题描述】:

很抱歉,这似乎是一个愚蠢的问题,但我真的觉得我在这里掉进了一个很深的兔子洞。

我很难理解超类的构造函数。我知道拥有一些所有子类都可以继承的函数和属性会很有用,但是假设我有一个所有动物都可以继承的类animal

它有一些所有动物都有的属性和方法。例如:

class Animal {
    bool alive;
    int weight;

    void reproduce();
};

这些显然是与所有类型的动物相关的少数几件事。

我知道将这些属性/方法放在超类中是多么有用,此外,我们可以为animals 创建与不同动物群体相关的子类,例如:

class Mammal : public animal {
    bool hasGivenBirth;

    void getPregnant();
};

然后去他们的性别等等。

但是,我们真的会构造一个AnimalMammal 吗?我不明白这有什么意义,因为所有动物都属于一个非常具体的子类别。

因此我的问题是超类中的构造函数如何有意义,我将如何使用它们?

【问题讨论】:

  • 当你构造一个子类类型的对象时,它必须初始化它的基类对象,这需要一个构造函数。如果您想禁止将这些类构造为基类对象以外的类,则可以将构造函数设为protected

标签: inheritance constructor overloading


【解决方案1】:

构造函数是一种可以确定在构造类实例时调用的方法,因为该行为是强制执行的。因此,您可以将代码放入构造中以确保该类已准备好使用

在超类的情况下,尽管您可能不直接构造一个超类,但您的子类可以接收一些参数(或者您可以硬编码它们)并将它们传递给父构造函数以执行操作,而不是自己执行操作。

在您的动物案例中,假设动物有许多条腿。所有动物都这样做,所以它是 Animal 的属性,而不是 Dog 的属性。如果你的狗构造函数可以访问它,它可以设置腿的数量,或者如果它没有,它可以调用基本构造函数传递腿的数量。

通过声明 Aninal 有一个要求腿数的构造函数,您永远不会忘记创建一个省略腿数的子类,因为您将无法在不提供基础构造函数的情况下调用它

class Animal{
  Animal(int numberOfLegs){}
}

class Dog:Animal{
  Dog(string breed): base(4) {} //call base constructor
}

class Cat:Animal{} //doesn't work; error "no argument is given that corresponds to formal parameter 'numberOfLegs'"

如果您没有在特定类中提供构造函数,编译器会默默地为您插入一个空白的无参数构造函数,除了调用基本无参数构造函数之外什么也不做。如果您确实提供了一个构造函数,它不会创建一个空白的。这就是导致错误“没有给定参数对应......”的原因 - 编译器已将一个构造函数自动插入到调用 Animal() 的 Cat 中,但没有无参数的构造函数,编译器不会猜测什么 int提供 numberOfLegs。

因此,因为 Animal 有一个需要多条腿的构造函数,我们不得不以某种方式为它提供一个值(我们可以在 Dog 的构造函数中接受一个参数以允许外部代码声明腿的数量) - 在本质上,我们不能构造 Animal 的任何子类,除非我们在调用 base(some_int_here) 的子类中创建构造函数。

如果动物在没有设置腿数的情况下无法工作,则此机制可确保设置腿数(为简洁起见,我没有显示在 Animal 内部分配腿数的设置器代码)

因此,是的,超类应该有构造函数——所有类都应该(并且确实)有构造函数(这是“法律”)。有时构造函数是空白的,有时它们会做一些事情

您还说您看不到我们如何构建一个简单的动物而不是特定的狗、猫等

您是对的,我们可能不会……但是 OO 的美妙之处在于我们可以使用 Animal 来泛指它的所有子代。我们可以制作一个 Animal 数组,将 10 种不同的特定动物放入其中,并使用它们的共同属性和方法,而无需知道或关心它们是什么。在现实世界中,我们一直通过使用它们的共同属性来处理事物。我从来没有开过梅赛德斯,但它的工作原理可能与我的福特车大致相同,踏板在同一个地方,并且在推动时做同样的事情。我们并不总是关心生活中每件物品的具体细节,福特有一个电缆离合器,而默克有一个液压离合器;当我们踩下踏板时,它的工作原理是一样的。类似地,我们的 Animal 数组,我们可以遍历它并将腿的数量相加,而无需知道每种动物的类型

Animal[] x = new Animal[10]();
x[0] = new Dog();
...

这不是构造动物,而是构造狗,但作为过程的一部分调用动物的构造函数。最后,我们可以通过访问动物的 NumberOfLegs 来添加腿,而无需说“如果数组元素是狗,则添加 4 else if ...”

这就是为什么基类有属性,还有构造函数(因为它们可能也需要一些设置)

【讨论】:

  • 一个非常彻底的解释,非常感谢,现在完全有意义。
猜你喜欢
  • 2015-12-18
  • 1970-01-01
  • 2015-02-16
  • 2013-08-08
  • 2017-06-07
  • 2011-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多