#include <iostream>
#include <stdio.h>
using namespace std;
// Base class
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
Shape()
{
printf("creating shape \n");
}
Shape(int h,int w)
{
height = h;
width = w;
printf("creatig shape with attributes\n");
}
protected:
int width;
int height;
};
// Derived class
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
Rectangle()
{
printf("creating rectangle \n");
}
Rectangle(int h,int w)
{
printf("creating rectangle with attributes \n");
height = h;
width = w;
}
};
int main(void)
{
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
Rectangle *square = new Rectangle(5,5);
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
return 0;
}
程序的输出如下:
creating shape
creating rectangle
creating shape
creating rectangle with attributes
Total area: 35
构造两个派生类对象时,我发现默认情况下始终是首先调用的基类构造函数。是否有一个原因?这就是为什么像python这样的语言坚持基类构造函数的显式调用而不是像C ++这样的隐式调用的原因?
答案 0 :(得分:60)
对此的简短回答是,&#34;因为这是C ++标准指定的内容&#34;。
请注意,您始终可以指定与默认值不同的构造函数,如下所示:
class Shape {
Shape() {...} //default constructor
Shape(int h, int w) {....} //some custom constructor
};
class Rectangle : public Shape {
Rectangle(int h, int w) : Shape(h, w) {...} //you can specify which base class constructor to call
}
只有在您没有指定要调用的基类时,才会调用基类的默认构造函数。
答案 1 :(得分:12)
除非在派生类中显式调用另一个构造函数,否则将调用默认的类构造函数。语言指定了这个。
Rectangle(int h,int w):
Shape(h,w)
{...}
将调用其他基类构造函数。
答案 2 :(得分:4)
构造对象时,始终首先构造基类子对象,因此,首先调用基类构造函数,然后调用派生类构造函数。原因是派生类对象包含从基类继承的子对象。您始终需要调用基类构造函数来初始化基类子对象。我们通常在派生类的成员初始化列表上调用基类构造函数。如果不显式调用基类构造函数,则编译将调用基类的默认构造函数来初始化基类子对象。但是,默认构造函数的隐式调用并不一定必须工作(例如,如果基类定义了一个不带参数就无法调用的构造函数)。
当对象超出范围时,它将首先调用派生类的析构函数,然后调用基类的析构函数。
答案 3 :(得分:1)
在c ++中,编译器始终确保成功调用对象层次结构中的函数。这些函数是构造函数和析构函数,对象层次结构是指继承树。
根据这条规则,我们可以猜测编译器会为继承层次结构中的每个对象调用构造函数和析构函数,即使我们没有实现它。为了执行此操作,编译器将为我们合成未定义的构造函数和析构函数,并将它们命名为默认构造函数和析构函数。然后,编译器将调用基类的默认构造函数,然后调用派生类的构造函数。
在你的情况下,你不调用基类构造函数,但是编译器通过调用基类的默认构造函数为你做这个,因为如果编译器没有这样做,你的示例中的Rectangle派生类将不完整,它可能会导致灾难,因为您可能会在派生类中使用基类的某些成员函数。因此,为了安全起见,编译器总是需要所有构造函数调用。
答案 4 :(得分:0)
想象一下:当您的子类从超类继承属性时,它们不会神奇地出现。您仍然需要构造对象。所以,你调用基础构造函数。想象一下,如果你的类继承了一个变量,你的超类构造函数会将其初始化为一个重要的值。如果我们不这样做,您的代码可能会失败,因为该变量未初始化。
答案 5 :(得分:0)
为什么基类&#39;默认构造函数被调用?事实证明并非总是如此。可以从派生类调用基类的任何构造函数(具有不同的签名)。构造函数。在您的情况下,默认构造函数被调用,因为它没有参数,所以它是默认的。
创建派生类时,调用构造函数的顺序始终为Base - &gt;派生在层次结构中。如果我们有:
class A {..}
class B : A {...}
class C : B {...}
C c;
当c为create时,首先调用A的构造函数,然后调用B的构造函数,然后调用C的构造函数。
保证订单,当派生类&#39;构造函数被调用,它总是调用基类&#39;派生类之前的构造函数&#39;构造函数可以做任何其他事情。出于这个原因,程序员可以手动调用基类&#39;构造函数在派生类的唯一初始化列表中。构造函数,具有相应的参数。例如,在以下代码中,Derived的默认构造函数将调用Base的构造函数Base :: Base(int i)而不是默认构造函数。
Derived() : Base(5)
{
}
如果在派生类的初始化列表中没有调用这样的构造函数&#39;构造函数,然后该程序假定一个基类&#39;没有参数的构造函数。这就是为什么调用没有参数的构造函数(即默认构造函数)的原因。