JHnet
JHnet

Reputation: 471

Force sub-classes to implement and call (once) a method

I am looking for a way to create a method that has to be implemented by every subclass. I also want the subclass to call this method on construction.

It should not be possible to call this method again after class construction..

#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;
}

This is an example, but it is not valid, because it calls a pure virtual method in constructor. Of course I can add init() in every subclass-constructor, but then it can be forgotten on new subclasses.

What is the C++ way of doing this?

Upvotes: 3

Views: 1068

Answers (5)

walrii
walrii

Reputation: 3522

C++11's call_once gets you most of the way, but it has costs.

  1. The class will not be movable nor copyable.
  2. You must add an extra line in every function that requires the initialization.

It does not prevent the method from being called more than once, but that is easy to add.

#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;
};

Notice that the Derived class has nothing special, except that it provides v_init.

struct Derived : Base {

    Derived() {
        std::cout << "Derived ctor" << std::endl;
    }

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

Demo code

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;
}

Output: Notice that v_init will be called which ever sample function is called first and is not called in the 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

Upvotes: 0

Serge Ballesta
Serge Ballesta

Reputation: 149075

AFAIK, it is very dangerous to call virtual functions in constructors. Here is a simple example. I slightly modified your code to have init method also implemented in Base class :

#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;
}

and the output is :

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

What can we conclude of that :

  • once the object is constructed, all is fine and when we call init method we normaly get the correct implementation from derived class
  • but in constructor, the order is :

    • call Base constructor
    • call init method from Base object (since derived object in not still constructed)
    • call DerivedX constructor

    So the method is always the one from Base which is definitively not what you expected.

Upvotes: 2

Mark Shevchenko
Mark Shevchenko

Reputation: 8207

I faced similar problem and could not find a simple solution. I had to make the initialization in a separate class. An object of this class can be passed to Base/Derive constructors, or this class can be a template parameter.

class Initializer {
    . . .
}

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

Or:

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

Sorry, I did not write in C++ too long, so I could prevent some syntax errors.

Upvotes: 0

mbgda
mbgda

Reputation: 797

As the other poster said, you should probably stay away from this, but the easiest example would be to make a public, non-virtual interface method on Base called Init() that must be called after the object is constructed. That method can call a pure-virtual "DoInit" method on the derived classes, and track whether or not it has been called yet with an internal flag.

I don't recommend this, but it will work.

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};
};

Upvotes: 0

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385264

The C++ way is to not do this. Init functions are bad. Simply use the constructors.

Upvotes: 4

Related Questions