#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++这样的隐式调用的原因吗?
7条答案
按热度按时间lrpiutwd1#
对此的简短回答是,“因为这是C++标准所规定的”。
请注意,您始终可以指定不同于默认值的构造函数,如下所示:
只有当您没有指定要调用的构造函数时,才会调用基类的默认构造函数。
jfewjypa2#
除非显式调用派生类中的另一个构造函数,否则将调用默认类构造函数。语言对此有规定。
将调用其他基类构造函数。
bjg7j2ky3#
构造对象时,总是先构造基类子对象,因此,基类构造函数先被调用,然后再调用派生类构造函数。原因是派生类对象包含从基类继承的子对象。总是需要调用基类构造函数来初始化基类子对象。我们通常在派生类的成员初始化列表上调用基类构造函数。如果没有显式调用基类构造函数,编译器将调用基类的默认构造函数来初始化基类子对象。但是,默认构造函数的隐式调用并不需要一直工作(例如,如果基类定义了一个没有参数就无法调用的构造函数)。
当对象超出作用域时,它会先调用派生类的析构函数,然后调用基类的析构函数。
zpgglvta4#
为什么调用基类的默认构造函数?事实证明情况并非总是如此。基类的任何构造函数(具有不同签名)都可以从派生类的构造函数调用。在您的示例中,调用默认构造函数是因为它没有参数,所以它是默认的。
当创建派生类时,在层次结构中调用构造函数的顺序总是Base -> Derived。如果我们有:
创建c时,首先调用A的构造函数,然后调用B的构造函数,然后调用C的构造函数。
为了保证这个顺序,当调用派生类的构造函数时,它总是在派生类的构造函数可以执行其他操作之前调用基类的构造函数。出于这个原因,程序员可以手动调用基类的构造函数的唯一初始化列表中的基类构造函数,并使用相应的参数。例如,在下面的代码中,Derived的默认构造函数将调用Base的构造函数Base::Base(int i),而不是默认构造函数。
如果在派生类的构造函数的初始化列表中没有调用这样的构造函数,那么程序假定基类的构造函数没有参数。这就是为什么一个没有参数的构造函数(即默认构造函数)。
bvpmtnay5#
在C++中,编译器总是确保对象层次结构中的函数被成功调用。这些函数是构造函数和析构函数,对象层次结构表示继承树。
根据这个规则我们可以推测编译器会为继承层次结构中的每个对象调用构造函数和析构函数,即使我们没有实现它。为了执行这个操作,编译器会为我们合成未定义的构造函数和析构函数,我们将它们命名为默认的构造函数和析构函数。然后,编译器会调用基类的默认构造函数,然后调用派生类的构造函数。
在你的例子中,你不调用基类的构造函数,但是编译器通过调用基类的默认构造函数来为你做,因为如果编译器不这样做,你的派生类(在你的例子中是Rectangle)将不完整,这可能会导致灾难,因为你可能会在派生类中使用基类的一些成员函数。因此,为了安全起见,编译器总是需要所有的构造函数调用。
pxy2qtax6#
想象一下:当你的子类从超类继承属性时,它们不会神奇地出现。你仍然需要构造对象。因此,您调用基构造函数。想象一下,如果你的类继承了一个变量,你的超类构造函数将它初始化为一个重要的值。如果我们不这样做,代码可能会失败,因为变量没有初始化。
mgdq6dx17#