构造函数是一种可以确定在构造类实例时调用的方法,因为该行为是强制执行的。因此,您可以将代码放入构造中以确保该类已准备好使用
在超类的情况下,尽管您可能不直接构造一个超类,但您的子类可以接收一些参数(或者您可以硬编码它们)并将它们传递给父构造函数以执行操作,而不是自己执行操作。
在您的动物案例中,假设动物有许多条腿。所有动物都这样做,所以它是 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 ...”
这就是为什么基类有属性,还有构造函数(因为它们可能也需要一些设置)