【问题标题】:C++ calling base class constructorsC++ 调用基类构造函数
【发布时间】:2013-03-24 13:05:46
【问题描述】:
#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++ 这样的隐式调用的原因吗?

【问题讨论】:

  • 继承层次结构中的每个构造函数都被调用,顺序是 Base -> Derived。析构函数以相反的顺序被调用。
  • 我的问题是“它总是被调用的基类的默认构造函数吗?”
  • @liv2hak 但在我看来 Rectangle(int h,int w) 构造函数在第二个 Rectangle 初始化时被调用...
  • @Kupto Rectangle 不是基类。 Shape 是基类。

标签: c++ object inheritance


【解决方案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)
{      
}

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

【讨论】:

    【解决方案2】:

    对此的简短回答是,“因为这是 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
    
    }
    

    只有在你不指定调用哪一个的情况下才会调用基类的默认构造函数。

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

          • @liv2hak 如果您的基类定义了可以不带参数调用的构造函数,在这种情况下,编译器不会为您生成默认构造函数,因此编译器将无法初始化基类类子对象,这将导致错误。所以它并不总是有效。
          【解决方案6】:

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

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

          会调用另一个基类的构造函数。

          【讨论】:

            猜你喜欢
            • 2015-08-18
            • 2018-05-25
            • 2012-09-28
            • 1970-01-01
            • 2016-07-19
            • 2011-02-12
            • 2018-07-21
            • 2020-11-28
            • 1970-01-01
            相关资源
            最近更新 更多