【问题标题】: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的?

【问题讨论】:

    标签: c++ c++11 inheritance parameters constructor


    【解决方案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() : initWithVal{false} { }
        Parameter(T&& val) : initWithVal{true}, valInit{std::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,或类似的东西。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-08-10
        • 1970-01-01
        • 1970-01-01
        • 2016-05-31
        • 2011-10-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多