【问题标题】:Calling the constructor of the base class after some other instructions in C++在 C++ 中的一些其他指令之后调用基类的构造函数
【发布时间】:2011-08-19 04:36:42
【问题描述】:

据我所知,不能调用基类的构造函数。我知道的唯一方法是:

MyClass::MyClass(/* args */) : Base(/* args */)
{
   // ...
}

但这会在开始时调用构造函数。 有没有办法在构造函数的其他地方调用它?像这样的:

MyClass::MyClass(/* args */)
{
   // ... instructions
   Base::Base(/* args */);
   // ... other_instructions
}

根据这个What are the rules for calling the superclass constructor? 问题,我知道没有办法,但我读了here,我猜这是可能的,但如果我尝试,我会得到:

error: invalid use of 'class Base'.

我做错了吗?是否有可能以某种方式做到这一点,或者是否有任何其他可能的解决方案来满足这一需求?

谢谢!

编辑:我知道我忘记了一个关键点:基类是框架的一部分,因此如果可能的话,最好不要修改它。

【问题讨论】:

  • 问题是:你为什么需要这个?
  • 由于某种原因我无法重新标记这个问题,我认为将 OOP 作为标签添加是合适的。
  • 我需要这个,因为我需要修改Qt框架的一个类,但是我想避免不得不直接修改Qt源代码。似乎小部件的构造函数做了一些导致调用 Qt 驱动程序中的某些方法的事情。在此调用发生之前,我希望我添加的其他一些字段已经初始化。

标签: c++ oop class


【解决方案1】:

如果基类构造函数至少有一个参数,您可以使用这样的辅助函数:

int DoStuffBeforeCtorAndForwardInt(int arg, Foo foo)
{
    DoStuff(arg, foo);
    return arg;
}

MyClass::MyClass(int arg, Foo foo)
    : Base(DoStuffBeforeCtorAndForwardInt(arg, foo))
{
   // ...
}

如果你想默认初始化基类,你可以使用copy-ctor复制一个默认初始化的基类实例:

Base DoStuffBeforeCtorAndReturnDefaultBase(int arg, Foo foo)
{
    DoStuff(arg, foo);
    return Base();
}

MyClass::MyClass(int arg, Foo foo)
    : Base(DoStuffBeforeCtorAndReturnDefaultBase(arg, foo))
{
   // ...
}

或者,如果Base 不必是第一个基类,您可以从辅助类派生MyClass

MyClass::MyClass(/* ... */)
    : DoStuffHelperClass(/* ... */),
    Base(/* ... */)
{
   // ...
}

以上所有要求你所做的“东西”不依赖于即将被初始化的对象(即函数不能安全地成为成员函数,你不能安全地将this作为参数传递给它们要么)。

这意味着您可以执行一些日志记录或类似操作,但同样您也可以在基类初始化后执行此操作。

编辑除了 DoStuffHelperClass 解决方案,您当然可以在 DoStuffHelperClass 中拥有成员,访问它们等等)


虽然我不得不说我不记得曾经使用/需要/想要这样的东西。对于您正在尝试做的事情,很可能还有另一种(首选)解决方案。

【讨论】:

  • 我不知道这些解决方案是否真的适用于我正在尝试做的事情,但无论如何,在我看来,这些解决方案中的每一个都是我问题的绝佳答案!谢谢!
【解决方案2】:

使用base-from-member idiom 在“真实”基类(即 Base)的 ctor 之前运行您的代码:

struct Base {
  Base(string, int);
};

struct DerivedDetail {
  DerivedDetail() {
    value = compute_some_value();
    value += more();
    value += etc();
    other = even_more_code(value);
  }
  string value;
  int other;
};

struct Derived : private DerivedDetail, Base {
  Derived() : Base(value, other) {}
  // In particular, note you can still use this->value and just
  // ignore that it is from a base, yet this->value is still private
  // within Derived.
};

即使您在 DerivedDetail 中没有所需的实际成员,这也有效。如果您在 Base 的 ctor 之前详细说明您必须做什么,那么我可以举一个更好的例子。

【讨论】:

    【解决方案3】:

    在您自己的类开始构建之前,基类始终是完全构建的。如果您需要对基类的状态进行更改,则必须在构建后明确地进行。

    例子:

    MyClass::MyClass()
    {
        // Implicit call to Base::Base()
    
        int result = computeSomething();
        Base::setResult(result);
    
        // ... 
    }
    

    【讨论】:

      【解决方案4】:

      除了已经写好的解决方案,还可以使用静态构造函数,将MyClass的构造函数设为私有。

      class QtBase{
        // ...
      };
      
      class MyClass : public QtBase{
      public:
        // copy ctor public
        MyClass(MyClass const& other);
      
        static MyClass Create(/*args*/){
          // do what needs to be done
          int idata;
          float fdata;
          // work with idata and fdata as if they were members of MyClass
          return MyClass(idata,fdata); // finally make them members
        }
      
        static MyClass* New(/*args*/){
          int idata;
          float fdata;
          // work with idata and fdata as if they were members of MyClass
          return new MyClass(idata,fdata); // finally make them members
        }
      
      private:
        // ctor private
        MyClass(int a_idata, float a_fdata)
          : idata(a_idata)
          , fdata(a_fdata)
        {}
      
        int idata;
        float fdata;
      };
      

      现在您必须创建MyClass 的实例:

      MyClass obj = MyClass::Create(/*args*/);
      

      MyClass* ptr = MyClass::New(/*args*/);
      

      【讨论】:

      • 顺便说一句:这就是所谓的“命名构造函数”;)
      • 如果我理解正确,如果我不需要初始化子类的成员,这将是一个解决方案。我不能用这个解决方案做到这一点,对吧?谢谢!
      • @Luca:嗯,你总是可以有一个构造函数,它的成员已经初始化。 :)
      • 对不起,这意味着我不理解这个解决方案 :-) 如果我在 return 语句中实例化 MyClass,在“做需要做的事情”中,我无法访问成员MyClass 的实例。但也许我完全没有抓住重点......你能举个例子吗?感谢您的帮助!
      • @Luca:稍微编辑了答案,添加了虚拟成员数据。我的意思是,您准备成员数据并将其传递给构造函数,如 cmets 所示。
      【解决方案5】:

      不,因为它不是类型安全的。
      考虑你有:一个类A 和一个变量A.var
      现在考虑B 继承自A,并在A 初始化之前使用var。你会得到一个运行时错误!语言想要防止这种情况发生,因此必须首先初始化超类构造函数。

      【讨论】:

        【解决方案6】:

        不,你不能那样做,正如其他人在之前的回答中所描述的那样。

        你唯一的机会是组合,IOW MyClass 使用 Base 类作为成员字段:

        class MyClass {
        public:
           /** the methods... */
        
        private:
           Base* _base;
        };
        

        这样您就可以稍后在获得所需信息时初始化_base。无论如何,我不知道这是否适用于您的场景。

        【讨论】:

        • 不幸的是,我认为我唯一的可能是创建一个实际的子类。被调用的方法作为参数传递给我正在创建的实例,我需要我的子类。
        【解决方案7】:

        没有。这是不可能的,因为构造函数调用的顺序是由标准严格定义的。必须先执行基类 ctor,然后才能执行派生类 ctor。

        【讨论】:

          猜你喜欢
          • 2015-07-20
          • 2013-01-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-08-18
          • 2020-08-17
          • 1970-01-01
          相关资源
          最近更新 更多