强制子类实现并调用(一次)方法

时间:2015-01-12 21:06:29

标签: c++

我正在寻找一种方法来创建一个必须由每个子类实现的方法。我还希望子类在构造时调用此方法。

在课程构建之后,不应该再次调用此方法。

#include <iostream>
class Base {
public:
    Base() {init();}
private:
    virtual void init() = 0;
};

class DerivedA : public Base {
public:
    DerivedA() {}
private:
    virtual void init() { std::cout << "Hello, I am A.";}
};

class DerivedB : public Base{
public:
    DerivedB() {}
private:
    virtual void init() {std::cout << "Hello, I am B.";}
};

int main(){
    DerivedA a;
    DerivedB b;
    return 0;
}

这是一个示例,但它无效,因为它在构造函数中调用纯虚方法。当然,我可以在每个子类构造函数中添加init(),但是在新的子类上可能会忘记它。

C ++的做法是什么?

5 个答案:

答案 0 :(得分:4)

C ++方式是这样做。 Init函数很糟糕。只需使用构造函数。

答案 1 :(得分:2)

AFAIK,在构造函数中调用虚函数是非常危险的。这是一个简单的例子。我稍微修改了您的代码,以便在init类中实现Base方法:

#include <iostream>
#include <exception>

class Base {
protected:
    Base() {init() ; }

    virtual void init() {
        std::cout << "Init base" << std::endl;
    }
public:
    void callinit() {
        init();
    }
};

class DerivedA : public Base {
public:
    DerivedA() {}
protected:
    virtual void init() { std::cout << "Hello, I am A."<< std::endl;}
};

class DerivedB : public Base{
public:
    DerivedB() {}
protected:
    virtual void init() {std::cout << "Hello, I am B."<< std::endl;}
};

int main(){
    DerivedA a;
    DerivedB b;
    a.callinit();
    b.callinit();
    return 0;
}

,输出为:

Init base
Init base
Hello, I am A.
Hello, I am B.

我们可以得出什么结论:

  • 一旦构造了对象,一切都很好,当我们调用init方法时,我们正常地从派生类中获得正确的实现
  • 但在构造函数中,顺序为:

    • 致电Base构造函数
    • 从Base对象调用init方法(因为尚未构造的派生对象)
    • 致电DerivedX构造函数

    因此,该方法始终是来自Base的方法,这绝对不是您所期望的。

答案 2 :(得分:0)

正如另一张海报所说,你应该远离这个,但最简单的例子是在Base上创建一个名为Init()的公共非虚拟接口方法,必须在构造对象后调用它。该方法可以在派生类上调用纯虚拟“DoInit”方法,并使用内部标志跟踪它是否已被调用。

我不推荐这个,但它会起作用。

class Base
{
public:
    void Init()
    {
        if(!initialized)
        {
            DoInit();
            initialized = true;
        }
    }
protected:
    virtual void DoInit() = 0; // derived classes need to implement this
private:
    bool initialized {false};
};

答案 3 :(得分:0)

我遇到了类似的问题,找不到简单的解决方案。我不得不在一个单独的类中进行初始化。此类的对象可以传递给Base / Derive构造函数,或者此类可以是模板参数。

class Initializer {
    . . .
}

class Base {
public:
    Base(Initializer* initializer) {
        // Get members from initializer
    }
}

或者:

template<Initializer TInitializer>
class Base<TInitializer> {
public:
    Base() {
        TInitializer initializer;
        // Get members from initializer
    }
}

抱歉,我没有用C ++写太长时间,所以我可以防止一些语法错误。

答案 4 :(得分:0)

C ++ 11的call_once可以帮到你,但它有成本。

  1. 该课程不可移动也不可复制。
  2. 您必须在每个需要初始化的函数中添加额外的行。
  3. 它不会阻止多次调用该方法,但这很容易添加。

    #include <iostream>
    #include <mutex>
    
    struct Base {
        Base() {
            std::cout << "Base ctor" << std::endl;
        }
    
        void sampleFunction1() {
            // this line must be at the start of every function that needs the initialization
            std::call_once(initedFlag, &Base::v_init, this);
    
            std::cout << "Base::sampleFunction1" << std::endl;
        }
    
        void sampleFunction2() {
            // this line must be at the start of every function that needs the initialization
            std::call_once(initedFlag, &Base::v_init, this);
    
            std::cout << "Base::sampleFunction2" << std::endl;
        }
    
    private:
        virtual void v_init() = 0;
        std::once_flag initedFlag;
    };
    

    请注意,Derived类没有任何特殊之处,只是它提供了v_init。

    struct Derived : Base {
    
        Derived() {
            std::cout << "Derived ctor" << std::endl;
        }
    
    private:
        void v_init() override {
            std::cout << "Derived::v_init" << std::endl;
        }
    };
    

    演示代码

    int main(int argc, const char * argv[]) {
        Derived d1;
        Derived d2;
        std::cout << "Calling d1" << std::endl;
        d1.sampleFunction1();
        d1.sampleFunction2();
        std::cout << "Calling d2" << std::endl;
        d2.sampleFunction2();
        d2.sampleFunction1();
        return 0;
    }
    

    输出:请注意,将调用v_init,首先调用样本函数,而不在ctors中调用。

    Base ctor
    Derived ctor
    Base ctor
    Derived ctor
    Calling d1
    Derived::v_init
    Base::sampleFunction1
    Base::sampleFunction2
    Calling d2
    Derived::v_init
    Base::sampleFunction2
    Base::sampleFunction1