从构造函数的主体调用超类构造函数

Posted

技术标签:

【中文标题】从构造函数的主体调用超类构造函数【英文标题】:Calling superclass constructor from constructor's body 【发布时间】:2018-01-18 11:17:21 【问题描述】:

我想创建一个类构造函数,它只会在特定条件下调用它的超类构造函数。我目前的实现如下所示。

class MyClass : public OtherClass

public:
    template<typename... Args>
    MyClass(bool condition, Args&&... args)
    
        if(condition)
        
            *(OtherClass*)this = OtherClass(args...);
        
        else
        
            //  Unrelated stuff
        
    
;

我不能在这里使用MyClass(...) : OtherClass(...) 语法,因为不应该每次都调用超类的构造函数。

有没有办法直接调用超类构造函数而不是调用move构造函数(如我的示例所示)。

【问题讨论】:

但是超类的构造函数总是会被调用 这行不通。在您进入子类 c'tor 的主体之前,必须始终调用基类 c'tor。你真正想要完成什么? This is an XY question. 听起来设计有点别扭,最好有两个构造函数用不同的参数调用超级构造函数? class MyClass : public OtherClass 表示“MyClassOtherClass”。没有OtherClass 就没有MyClass 条件是什么?如果可以在编译时知道它,您可能可以使用 SFINAE 执行此操作。 【参考方案1】:

您可以为基类和派生类创建 2 个不同的构造函数。 派生类的构造函数调用基类的相应构造函数。 派生类中的静态方法根据传递的参数创建一个实例。 像这样的:

class OtherClass

  public:

    OtherClass()
    
      ...
    

    OtherClass(Args&... args)
    
      ...
    

 // class OtherClass

class MyClass: public OtherClass

  private:

    MyClass(): OtherClass()
    
      ...
    

    MyClass(Args&... args): OtherClass(args)
    
      ...
    

  public:

    static MyClass* createInstance(bool      condition,
                                   Args&&... args)
    
      if (condition)
        return (new MyClass());
      else
        return (new MyClass(args));
    

 // class MyClass

【讨论】:

看来我的问题解释得很糟糕。我想让一个 c'tor 能够根据与它的 args 值相关的某些条件来决定是调用 OtherClass() 还是 OtherClass(some args)。但是,@StoryTeller 已经说过这是不可能的,因为应该首先调用超类 c'tor。 使用基于传递参数创建实例的静态方法。我更新了我的答案 更喜欢返回 smart_pointer (as std::unique_ptr) 而不是原始的拥有指针。【参考方案2】:

解决方案是不在构造函数中执行此操作,而是在辅助函数中执行此操作。例如:

class BaseClass 
    BaseClass() 
    BaseClass(int x) setX(x);
    void setX(int x) 
        //thing
    


class Derived : BaseClass 
    Derived(int x) 
        if (x>10) setX(x);
    

【讨论】:

【参考方案3】:

您可以通过以下方式重载构造函数来实现标签调度

struct PassArgs ;
struct DoNotPassArgs ;

class MyClass: public OtherClass 
public:
    template<typename... Args>
    MyClass(PassArgs, Args&&... args) : OtherClass(args...) 

    template<typename... Args>
    MyClass(DoNotPassArgs, Args&&...) : OtherClass() 
;

【讨论】:

【参考方案4】:

Robert Kock 的上述回答并不完全正确。他正在从派生类构造函数的初始化列表中调用基类(超类)构造函数,但没有条件。但根据您的问题can we call a base class constructor based on a CONDITION from the BODY of derived class constructor?。答案是否定的,我们不能。根据面向对象的原则,基类构造函数必须首先调用和初始化。在 Robert Kock 的上述回答中,在控制进入主体内部之前,从驱动类构造函数的初始化列表中调用了基类构造函数,并且没有任何条件。在初始化列表中你甚至不能放置任何条件。所以不可能调用基于类的构造函数来从派生类构造函数的主体中调用。这就是这种要求的原因,我们在一个类中引入了一个称为 init() 的方法。现在下面的示例可以完全满足您的要求。在此示例中,您必须在基类中添加一个默认构造函数,以便首先调用此默认构造函数,但在该默认构造函数中您不会做任何事情。

现在看下面的例子:-

#include<iostream>

class B

  private:
  int x;
  int y;

  public:
  B()std::cout<<"I am B's default constructor"<<std::endl;//Default constructor not doing anything
  void init(int x)
  
    std::cout<<"Based init with param x"<<std::endl;
    this->x = x; //this method initializing only one member
    y = 0;// just initialized with default value 0
  
  void init(int x,int y)
  
    std::cout<<"Based init with param x and y"<<std::endl;
    this->x = x; // here we initializing all the members
    this->y = y;
  
  void print()
  
    std::cout<<"x ="<<x<<std::endl;
    std::cout<<"y ="<<y<<std::endl;
  
;

class D : public B

  public:
  D(int i)
  
    std::cout<<"I am D's constructor"<<std::endl;
    if( i == 1 )
       B::init(3);
    else
       B::init(4, 5);
  
;

int main()

   std::cout<<"First Example"<<std::endl;
   D *d = new D(1);
   d->print();
   std::cout<<"Now second Example"<<std::endl;
   D *d1 = new D(2);
   d1->print();
   return 0;
 

【讨论】:

以上是关于从构造函数的主体调用超类构造函数的主要内容,如果未能解决你的问题,请参考以下文章

什么时候需要显式调用超类构造函数?

为啥总是调用超类构造函数[重复]

通过生成构造函数将超类注入原始对象

通过生成构造函数将超类注入原始对象

有没有在 C++ 中不调用超类的构造函数的时候?

在Java中_not_调用超类构造函数的任何方法?