c++调用基类构造函数

C++ calling base class constructors

本文关键字:构造函数 基类 调用 c++      更新时间:2023-10-16
#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 attributesn");
      } 
   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++这样的隐式调用的原因吗?

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

基类的默认构造函数只有在没有指定调用的情况下才会被调用。

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

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

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

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

当对象超出作用域时,首先调用派生类的析构函数,然后调用基类的析构函数

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

创建派生类时,在层次结构中调用构造函数的顺序始终是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)
{      
}

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

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

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

在你的情况下,你不调用基类的构造函数,但编译器通过调用基类的默认构造函数为你做,因为如果编译器没有这样做,你的派生类矩形在你的例子中将不完整,它可能会导致灾难,因为你可能会在你的派生类中使用一些基类的成员函数。因此,为了安全起见,编译器总是需要所有的构造函数调用。

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