【问题标题】:How do I call the base class constructor?如何调用基类构造函数?
【发布时间】:2021-03-14 18:24:37
【问题描述】:

最近,我用 Java 做了很多编程。在那里,你用super(). 调用你继承的类(你可能都知道。)

现在我有一个 C++ 类,它有一个带有一些参数的默认构造函数。示例:

class BaseClass {
public:
    BaseClass(char *name); .... 

如果我继承了这个类,它会警告我没有合适的默认构造函数可用。那么,C++ 中是否有类似super() 的东西,还是我必须定义一个函数来初始化所有变量?

【问题讨论】:

  • C++ 中没有 super(),部分原因是你可以有多个基类。
  • MSVC 中有__super 关键字。它甚至适用于多个基类,并且仅在存在歧义时才会发出错误。
  • @Ajay 这是一个 MSVC 扩展,而不是标准 c++
  • @VJ,是的,我明确提到了 MSVC。初始下划线表示它不是标准关键字。 :)
  • 我会支持它,因为它可能对某人有所帮助,即使那个人不是我。这是一个很好的评论,因为它提供了相关知识。

标签: c++ constructor


【解决方案1】:

您在子类的构造函数的初始化列表中执行此操作。

class Foo : public BaseClass {
public:
    Foo() : BaseClass("asdf") {}
};

在初始化任何成员之前,必须在那里调用带有参数的基类构造函数。

【讨论】:

  • 好吧,为了迂腐,在初始化任何成员之前,基类构造函数在初始化列表中被调用;如果未提供基类,则它会尝试隐式使用默认构造函数。如果BaseClass有默认构造函数,并且初始化列表中没有其他构造函数,则调用默认的;并不是程序员必须调用它。
【解决方案2】:

在头文件中定义一个基类:

class BaseClass {
public:
    BaseClass(params);
};

然后定义派生类为继承BaseClass:

class DerivedClass : public BaseClass {
public:
    DerivedClass(params);
};

在源文件中定义 BaseClass 构造函数:

BaseClass::BaseClass(params)
{
     //Perform BaseClass initialization
}

默认情况下,派生构造函数只调用不带参数的默认基构造函数;所以在这个例子中,在调用派生构造函数时不会自动调用基类构造函数,但可以通过在冒号后添加基类构造函数语法来实现(:)。定义一个自动调用其基构造函数的派生构造函数:

DerivedClass::DerivedClass(params) : BaseClass(params)
{
     //This occurs AFTER BaseClass(params) is called first and can
     //perform additional initialization for the derived class
}

BaseClass 构造函数在 DerivedClass 构造函数之前调用,如果需要,可以将相同/不同的参数 params 转发到基类。这可以嵌套在更深的派生类中。派生构造函数必须调用完全一个基构造函数。析构函数按照调用构造函数的相反顺序自动调用。

编辑:如果您从任何virtual 类继承,则此规则有一个例外,通常是为了实现多重继承菱形继承。然后你必须显式调用所有virtual 基类的基构造函数并显式传递参数,否则它只会调用它们的默认构造函数没有任何参数。见:virtual inheritance - skipping constructors

【讨论】:

    【解决方案3】:

    你必须使用初始化器:

    class DerivedClass : public BaseClass
    {
    public:
      DerivedClass()
        : BaseClass(<insert arguments here>)
      {
      }
    };
    

    这也是您构造没有构造函数(或您想要初始化)的类成员的方式。任何未提及的成员都将被默认初始化。例如:

    class DerivedClass : public BaseClass
    {
    public:
      DerivedClass()
        : BaseClass(<insert arguments here>)
        , nc(<insert arguments here>)
        //di will be default initialized.
      {
      }
    
    private:
      NeedsConstructor nc;
      CanBeDefaultInit di;
    };
    

    指定成员的顺序无关紧要(尽管构造函数必须先出现),但构造它们的顺序是声明顺序。所以nc总是在di之前构造。

    【讨论】:

      【解决方案4】:

      关于 super 的替代方案;在大多数情况下,您会在派生类的初始化列表中使用基类,或者在其他地方工作并且派生类重新定义数据成员时使用Base::someData 语法。

      struct Base
      {
          Base(char* name) { }
          virtual ~Base();
          int d;
      };
      
      struct Derived : Base
      {
          Derived() : Base("someString") { }
          int d;
          void foo() { d = Base::d; }
      };
      

      【讨论】:

        【解决方案5】:

        在初始化列表中使用基类的名称。 initializer-list 出现在方法体之前的构造函数签名之后,可用于初始化基类和成员。

        class Base
        {
        public:
          Base(char* name)
          {
             // ...
          }
        };
        
        class Derived : Base
        {
        public:
          Derived()
            : Base("hello")
          {
              // ...
          }
        };
        

        或者,某些人使用的模式是自己定义“超级”或“基础”。也许喜欢这种技术的一些人是正在转向 C++ 的 Java 开发人员。

        class Derived : Base
        {
        public:
          typedef Base super;
          Derived()
            : super("hello")
          {
              // ...
          }
        };
        

        【讨论】:

        • 我不会说typedefing super 很常见。在 20 多年的 C++ 中,我从未见过一个项目做到了这一点。
        • @James:同意。 Boost 邮件列表上有关于制作一个用于拥有super 的小实用程序的讨论,但没有任何结果。只是真的不是很有用。
        • 我已经看到它经常与 CRTP(奇怪的重复模板模式)一起使用,其中您派生的类是一个模板,将正在定义的类和其他一些策略类作为模板参数。由于基类的所有模板参数最终会变得非常冗长,因此对它进行 typedef 非常方便,这样您就可以避免每次要使用它时都将其全部写出来。而且,如果您想将基本类型更改为与当前基本类型兼容的鸭子类型,则可以通过更改 typedef 来实现,而不必更改太多代码。
        • 显然,在涉及多重继承的情况下,使用一个名称(例如 base 或 super)可能没有那么有用,但是有很多代码使用单继承,因此在实践中它可能很有用。我不会使用我发布的简单示例来使用 typedef 解决方案,但是当事情涉及更多时,我认为它可以很好地工作。
        【解决方案6】:

        C++ 中没有 super()。您必须通过名称显式调用基本构造函数。

        【讨论】:

          猜你喜欢
          • 2016-07-19
          • 2011-02-12
          • 2018-07-21
          • 2017-03-17
          • 2017-04-13
          • 2013-03-24
          • 2015-08-18
          • 2018-07-16
          相关资源
          最近更新 更多