59
#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++ 这样的隐式调用的原因吗?

4

6 回答 6

106

对此的简短回答是,“因为这是 C++ 标准所指定的”。

请注意,您始终可以指定与默认构造函数不同的构造函数,如下所示:

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

}

仅当您未指定要调用哪一个时,才会调用基类的默认构造函数。

于 2013-04-03T02:13:03.813 回答
25

除非您在派生类中显式调用另一个构造函数,否则将调用默认类构造函数。语言指定了这一点。

Rectangle(int h,int w):
   Shape(h,w)
  {...}

将调用其他基类构造函数。

于 2013-04-03T02:05:24.880 回答
5

构造对象时,总是先构造基类子对象,因此,先调用基类构造函数,再调用派生类构造函数。原因是派生类对象包含从基类继承的子对象。您总是需要调用基类构造函数来初始化基类子对象。我们通常在派生类的成员初始化列表上调用基类构造函数。如果不显式调用基类构造函数,编译将调用基类的默认构造函数来初始化基类子对象。但是,对默认构造函数的隐式调用不一定总是有效(例如,如果基类定义了一个没有参数就无法调用的构造函数)。

当对象超出范围时,它会先调用派生类的析构函数,然后调用基类的析构函数。

于 2013-04-03T02:03:05.750 回答
2

在 c++ 中,编译器始终确保对象层次结构中的函数被成功调用。这些函数是构造函数和析构函数,对象层次结构意味着继承树。

根据这个规则,我们可以猜测编译器会为继承层次结构中的每个对象调用构造函数和析构函数,即使我们没有实现它。为了执行这个操作,编译器会为我们合成未定义的构造函数和析构函数,我们将它们命名为默认构造函数和析构函数。然后,编译器会调用基类的默认构造函数,然后调用派生类的构造函数。

在您的情况下,您不调用基类构造函数,但编译器通过调用基类的默认构造函数为您执行此操作,因为如果编译器不这样做,您的派生类(在您的示例中为 Rectangle)将不完整,并且可能会导致灾难因为也许你会在派生类中使用基类的一些成员函数。所以为了安全编译器总是需要所有的构造函数调用。

于 2013-12-24T14:33:34.583 回答
1

为什么调用基类的默认构造函数?事实并非总是如此。基类的任何构造函数(具有不同的签名)都可以从派生类的构造函数中调用。在您的情况下,调用默认构造函数是因为它没有参数,所以它是默认的。

创建派生类时,在层次结构中调用构造函数的顺序始终是 Base -> Derived。如果我们有:

class A {..}
class B : A {...}
class C : B {...}
C c;

创建 c 时,首先调用 A 的构造函数,然后调用 B 的构造函数,然后调用 C 的构造函数。

为了保证这个顺序,当一个派生类的构造函数被调用时,它总是在派生类的构造函数可以做任何事情之前调用基类的构造函数。出于这个原因,程序员可以在派生类的构造函数的唯一初始化列表中手动调用基类的构造函数,并带有相应的参数。例如,在下面的代码中,Derived 的默认构造函数将调用 Base 的构造函数 Base::Base(int i) 而不是默认构造函数。

Derived() : Base(5)
{      
}

如果在派生类的构造函数的初始化列表中没有调用这样的构造函数,则程序假定一个没有参数的基类构造函数。这就是调用没有参数的构造函数(即默认构造函数)的原因。

于 2015-02-16T08:12:54.847 回答
0

想象一下:当您的子类从超类继承属性时,它们不会神奇地出现。您仍然必须构造对象。因此,您调用基本构造函数。想象一下,如果你的类继承了一个变量,你的超类构造函数将其初始化为一个重要的值。如果我们不这样做,您的代码可能会因为变量未初始化而失败。

于 2013-04-03T02:35:07.700 回答