很抱歉,这似乎是一个愚蠢的问题,但我真的觉得我在这里掉了一个深深的兔子洞。
我很难理解超类构造函数。我知道拥有所有子类都可以继承的少数函数和属性会很有用,但是假设我有一个animal
类,所有动物都可以继承。
它具有所有动物都具有的一些属性和方法。例如:
class Animal {
bool alive;
int weight;
void reproduce();
};
这些显然是与所有类型的动物有关的少数事物。
我知道将这些属性/方法放在一个超类中是多么有用,此外,我们可以为animals
生成与不同动物组相关的子类,例如:
class Mammal : public animal {
bool hasGivenBirth;
void getPregnant();
};
然后转到他们是性别等等。
但是,我们实际上会构造一个Animal
还是Mammal
吗?我不认为这有什么道理,因为所有动物都将落入一个非常特定的子类别。
因此,我的问题是超类中的构造函数如何有意义,以及如何使用它们?
答案 0 :(得分:1)
构造函数是可以确定在构造类实例时调用的方法,因为该行为是强制执行的。因此,您可以将代码放入构造中,以确保可以使用该类
对于超类,虽然您可能无法直接构造一个超类,但您的子类可以接收一些参数(或您可以对其进行硬编码)并将它们传递给父构造函数以执行操作,而不是自己执行操作。
以您的动物为例,假设动物有多条腿。所有动物都这样做,所以它是动物的财产,而不是狗的财产。您的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'"
如果您未在特定类中提供构造函数,则编译器将为您静默插入一个空白的无参数构造函数,该函数除了调用基本无参数构造函数外别无其他。如果提供了构造函数,则不会创建空白的构造函数。这就是引起错误“没有给定对应的参数...”的原因-编译器已将一个构造函数自动插入到Cat中,该构造函数调用Animal(),但没有无参数的构造函数,并且编译器不会猜测是什么int提供numberOfLegs。
因此,因为Animal的构造函数需要很多支脚,所以我们不得不以某种方式为其提供一个值(我们可以在Dog的构造函数中接受一个参数,以允许外部代码声明支脚的数量)-在本质上,除非我们在子类中创建调用base(some_int_here)
的构造函数,否则我们无法构造Animal的任何子类。
如果动物在未设置腿数的情况下无法工作,则该机制可确保设置了腿数(为简洁起见,我没有显示在动物内部内部分配腿数的设置员代码)
因此,是的,超类应该具有构造函数-所有类都应该(并且确实)具有构造函数(这是“法律”)。有时构造函数是空白的,有时它们会做一些事情
您还说过,您看不到我们怎么只构造简单的动物而不是特定的狗,猫等
您是对的,我们可能不是。.但是OO的优点在于,我们可以使用Animal来泛指其所有子项。我们可以制作一组动物,并在其中放置10种不同的特定动物,并在不知道或不关心它们是什么的情况下使用它们的共同属性和方法。在现实世界中,我们始终通过使用事物的共同属性来遗传对待事物。我从未驾驶过梅赛德斯,但它的工作原理与福特基本相同,在相同的位置踩踏板并在踩下时做同样的事情。我们并不总是在乎生活中每个对象的具体细节,因为福特有一个拉线离合器,而奔驰有一个液压离合器。当我们踩下踏板时,其作用相同。同样,我们的动物数组也可以遍历并增加腿数,而无需了解每种动物
Animal[] x = new Animal[10]();
x[0] = new Dog();
...
这不是构造动物,而是构造狗,但是动物的构造函数被称为过程的一部分。最后,我们可以通过访问动物的NumberOfLegs来添加腿,而无需说“如果array元素是一条狗,那么如果...则添加4 else”。
这就是为什么基类具有属性以及构造函数的原因(因为它们可能也需要一些设置)