从祖先的方法和构造函数调用后代的方法

Posted

技术标签:

【中文标题】从祖先的方法和构造函数调用后代的方法【英文标题】:Call descendant's method from ancestor's methods and constructor 【发布时间】:2016-09-26 19:09:32 【问题描述】:

接下来的代码展示了类参数包装器,它允许通过 getter 和 setter 访问底层类。简化版:

template<typename T>
class Parameter

public:
    typedef T value_type;
    typedef Parameter<T> Type;
protected:
    T value_;
public:
    Parameter()  this->value_ = this->default_value(); 
    Parameter(T&& val)  this->set(std::forward<T>(val)); 
    Parameter(const T& val)  this->set(std::forward<T>(val)); 
    virtual ~Parameter() ;

    // Get
    T& get()  return this->value_/*this->getter()*/; 
    operator T&()  return this->get(); 

    // Set
    Type& set(T&& val)
    
        std::cout << "T&& setter called with " << val << std::endl;
        value_ = this->setter(std::forward<T>(val));
        return *this;
    
    Type& set(const T& val)
    
        std::cout << "constT& setter called with " << val << std::endl;
        value_ = this->setter(std::forward<const T>(val));
        return *this;
    
    Type& operator=(T const& val)  return this->set(val); 
    Type& operator=(T&& val)  return this->set(val); 

    virtual T setter(T const& val)  return val; 

    virtual const T& default_value()
    
        static const T default_value_;
        return default_value_;
    ;
;

使用:

int main()

    struct IncrementorPar : Parameter<int>
    
        using Parameter::Parameter;  //todo HIDE
        using Parameter::operator=;  //todo HIDE
        virtual int setter(int const& val)  return val + 1; 
        virtual const int& default_value() return -1; ;
     in1(1), in2 = 2, int0;
    //assert(int0==int0.default_value());  //FAIL
    //assert(int0==-1);  //FAIL
    //assert(in1 == 2);  //FAIL
    //assert(in2 == 3);  //FAIL
    auto *pi1 = new IncrementorPar(2);
    //assert(*pi1==3);  //FAIL
    pi1->set(2);
    assert(*pi1==3);
    *pi1 = 33;

怎么可能从祖先的构造函数中调用后代的方法setter()default_value() 如何隐藏using的?

【问题讨论】:

【参考方案1】:

并不是一个真正优雅的解决方案,但是...

您可以使用init() 函数推迟value_ 的初始化。

类似

template<typename T>
class Parameter

private:
    bool  toInit  true ;
    bool  initWithVal;
    T     valInit;

    void init ()
     
       if ( initWithVal )
          this->set(valInit);
       else
          value_ = this->default_value();

       toInit = false;
     

public:
    typedef T value_type;
    typedef Parameter<T> Type;
protected:
    T value_;
public:
    Parameter() : initWithValfalse  
    Parameter(T&& val) : initWithValtrue, valInitstd::move(val)  
    Parameter(const T& val)  this->set(std::forward<T>(val)); 
    virtual ~Parameter() ;

    // Get
    T& get()  if ( toInit ) init(); return this->value_/*this->getter()*/; 
    operator T&()  return this->get(); 

    // Set
    Type& set(T&& val)
    
      toInit = false;
      std::cout << "T&& setter called with " << val << std::endl;
      value_ = this->setter(std::forward<T>(val));
      return *this;
    
    Type& set(const T& val)
    
      toInit = false;
      std::cout << "constT& setter called with " << val << std::endl;
      value_ = this->setter(std::forward<const T>(val));
      return *this;
    
    Type& operator=(T const& val)  return this->set(val); 
    Type& operator=(T&& val)  return this->set(val); 

    virtual T setter(T const& val)  return val; 

    virtual const T& default_value()
    
      std::cout << "base default value\n";
        static const T default_value_;
        return default_value_;
    ;
;

【讨论】:

【参考方案2】:

说明

如果不重写每个派生类的构造函数,就不可能从祖先调用后代的方法,因为在构造过程中,vtable 仅按照构造函数调用的顺序(其中我们记得,是基础 -> 最衍生)。

摘自forum:

我有一个类,它从它的构造函数中调用一个虚方法Init()。 在后代中,我覆盖了这个虚拟方法Init()。这个想法是 每当我创建后代时,我希望后代的Init() 是 叫,不是祖宗!而且我不想覆盖构造函数 那是因为我必须重写每个后代的构造函数 然后。这怎么可能?

回答

不重写每个派生类的构造函数是不可能的, 在施工期间,vtable 仅提供有关 到目前为止构造的类,按照构造函数调用的顺序(正如我们 回想一下,是基础 -> 最衍生的)。所以当ClassA的构造函数被调用时, 尽管它是ClassB 的实例,但虚函数机制将 只能在之后访问ClassBClassA 函数的覆盖 ClassA 的构造函数已经执行完毕,所以任何时候你调用一个 virtual 将调用来自ClassA::ClassA()ClassA::Init() 的函数。 ——丹

解决方案

按照@max66 的建议使用init guides-variables,或类似的东西。

【讨论】:

以上是关于从祖先的方法和构造函数调用后代的方法的主要内容,如果未能解决你的问题,请参考以下文章

从同名的后代方法调用 QML 祖先方法

从构造函数调用的异步方法[重复]

监视从组件的构造函数调用的方法

如何继承构造函数?

如何从同一个类中的另一个构造函数调用抽象类的构造函数(方法重载)[重复]

C++调用父类的构造函数规则