5

So I recently accidentally called some virtual functions from the constructor of a base class, i.e. Calling virtual functions inside constructors.

I realise that I should not do this because overrides of the virtual function will not be called, but how can I achieve some similar functionality? My use-case is that I want a particular function to be run whenever an object is constructed, and I don't want people who write derived classes to have to worry about what this is doing (because of course they could call this thing in their derived class constructor). But, the function that needs to be called in-turn happens to call a virtual function, which I want to allow the derived class the ability to override if they want.

But because a virtual function gets called, I can't just stick this function in the constructor of the base class and have it get run automatically that way. So I seem to be stuck.

Is there some other way to achieve what I want?

edit: I happen to be using the CRTP to access other methods in the derived class from the base class, can I perhaps use that instead of virtual functions in the constructor? Or is much the same issue present then? I guess perhaps it can work if the function being called is static?

edit2: Also just found this similar question: Call virtual method immediately after construction

Community
  • 1
  • 1
Ben Farmer
  • 2,387
  • 1
  • 25
  • 44
  • 4
    If the function call is necessary for constructing the derived class, and is specific to that class, it sounds like it should be called from the derived class's constructor. It's not a good idea to want to call methods from an object that doesn't technically exist yet. – Weak to Enuma Elish Feb 26 '16 at 17:55
  • Well the function I want to run is a kind of run-time verification routine, to make sure that the derived class they have written complies with certain requirements. It needs access to the virtual functions in order to check that they work properly. – Ben Farmer Feb 26 '16 at 18:21
  • In that case, a factory method like @Jarod42 proposes is probably your best bet. – Weak to Enuma Elish Feb 26 '16 at 18:25

6 Answers6

3

If really needed, and you have access to the factory.

You may do something like:

template <typename Derived, typename ... Args>
std::unique_ptr<Derived> Make(Args&&... args)
{
    auto derived = std::make_unique<Derived>(std::forward<Args>(args));
    derived->init(); // virtual call
    return derived;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

There is no simple way to do this. One option would be to use so-called virtual constructor idiom, hide all constructors of the base class, and instead expose static 'create' - which will dynamically create an object, call your virtual override on it and return (smart)pointer.

This is ugly, and what is more important, constrains you to dynamically created objects, which is not the best thing.

However, the best solution is to use as little of OOP as possible. C++ strength (contrary to popular belief) is in it's non-OOP specific traits. Think about it - the only family of polymorphic classess inside standard library are streams, which everybody hate (because they are polymorphic!)

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • I like streams :p. But hmm ok, I agree that solution sounds like a bad idea... not sure how to re-think this design though. – Ben Farmer Feb 26 '16 at 18:47
  • I've never had to worry about their speed. They are slow? – Ben Farmer Feb 26 '16 at 19:19
  • @BenFarmer, obnoxiously. – SergeyA Feb 26 '16 at 19:23
  • And the root cause is their polymorphism? – Ben Farmer Feb 26 '16 at 19:30
  • @BenFarmer, yep. Terribly slow. – SergeyA Feb 26 '16 at 19:30
  • "This [...] constrains you to dynamically created objects, which is not the best thing." - Placement new is a thing; if the default constructor was cheap then the factory could just take the address of a (possibly automatic) buffer, or a member variable, and recreate a new object there. – Kuba hasn't forgotten Monica Feb 26 '16 at 19:43
  • 1
    @KubaOber, makes it even more terrible. Than you need to manually call the destructor... Just don't do it. – SergeyA Feb 26 '16 at 19:50
  • @SergeyA Calling the destructor isn't a big deal - presumably the destructor on a default-constructed object would do nothing much. Best of all, it'd be all done inside of the factory function. Furthermore, the factory function can simply be a templated function that can create any derived class, taking an optional pointer if placement construction is desired. – Kuba hasn't forgotten Monica Feb 26 '16 at 19:56
  • @KubaOber, how so? You need an object, so your first prepare a buffer and call your factory. Factory does inplace construction. Now, you end up with an object - and it is **you** who must call the destructor. Factory can't help you here. And it is a **big deal** - a big part of C++ is an automatic deterministic destruction. Take this away and you end up with a very unpleasant language. – SergeyA Feb 26 '16 at 19:58
  • @SergeyA If the buffer is an automatic storage variable or a class member, it'll be default-constructed into an unusable state, and the factory will re-construct it to a usable state. The compiler then invokes the necessary destructor. If the factory is called without a buffer, it'll dynamically allocate and construct it via the usual use of `new`, and you can `delete` it manually or through a smart pointer. So it's not that bad. – Kuba hasn't forgotten Monica Feb 26 '16 at 20:05
  • @KubaOber, no, this where you are very confused. Compiler will not call the destructor when placement new was used. Someone with your rep should know that. – SergeyA Feb 26 '16 at 20:15
  • @SergeyA Remember that the class is already default constructed before the factory gets a go at re-constructing it "the proper way" :) The sequence would be: default constructor, factory { virtual destructor, placement new }. The default-constructed object can have any kind of storage. – Kuba hasn't forgotten Monica Feb 26 '16 at 20:19
  • @KubaOber, I see what you mean. But this would require one to open up the default constructor, and the whole excercise becomes absolutely moot - you will be relying on programmers discipline to call your factory after default constructing, but than you can just mandate two-step initialization and rely on programmers discipline to follow this rule. The whole point was to **not** rely on programmers discipline. – SergeyA Feb 26 '16 at 20:25
  • @SergeyA The factory would not be invoked manually, but still I offered a less hacky solution in a separate answer. – Kuba hasn't forgotten Monica Feb 26 '16 at 21:17
1

If you take a look at how others solved this problem, you will notice that they simply transferred the responsibility of calling the initialization function to client. Take MFC’s CWnd, for instance: you have the constructor and you have Create, a virtual function that you must call to have a proper CWnd instantiation: “these are my rules: construct, then initialize; obey, or you’ll get in trouble”.

Yes, it is error prone, but it is better than the alternative: “It has been suggested that this rule is an implementation artifact. It is not so. In fact, it would be noticeably easier to implement the unsafe rule of calling virtual functions from constructors exactly as from other functions. However, that would imply that no virtual function could be written to rely on invariants established by base classes. That would be a terrible mess.” - Stroustrup. What he meant, I reckon, is that it would be easier to set the virtual table pointer to point to the VT of derived class instead of keep changing it to the VT of current class as your constructor call goes from base down.

I realise that I should not do this because overrides of the virtual function will not be called,...

Assuming that the call to a virtual function would work the way you want, you shouldn't do this because of the invariants.

class B // written by you
{
public:
  B() { f(); }
  virtual void f() {}
};

class D : public B // written by client
{
  int* p;
public:
  D() : p( new int ) {}
  void f() override { *p = 10; } // relies on correct initialization of p  
};

int main()
{
  D d;
  return 0;
}

What if it would be possible to call D::f from B via VT of D? You will use an uninitialized pointer, which will most likely result in a crash.

...but how can I achieve some similar functionality?

If you are willing to break the rules, I guess that it might be possible to get the address of desired virtual table and call the virtual function from constructor.

zdf
  • 4,382
  • 3
  • 18
  • 29
0

I want a particular function to be run whenever an object is constructed, [... it] in-turn happens to call a virtual function, which I want to allow the derived class the ability to override if they want.

This can be easily done if you're willing to live with two restrictions:

  1. the constructors in the entire class hierarchy must be non-public, and thus
  2. a factory template class must be used to construct the derived class.

Here, the "particular function" is Base::check, and the virtual function is Base::method.

First, we establish the base class. It has to fulfill only two requirements:

  1. It must befriend MakeBase, its checker class. I assume that you want the Base::check method to be private and only usable by the factory. If it's public, you won't need MakeBase, of course.
  2. The constructor must be protected.

https://github.com/KubaO/stackoverflown/tree/master/questions/imbue-constructor-35658459

#include <iostream>
#include <utility>
#include <type_traits>
using namespace std;

class Base {
   friend class MakeBase;
   void check() {
      cout << "check()" << endl;
      method();
   }
protected:
   Base() { cout << "Base()" << endl; }
public:
   virtual ~Base() {}
   virtual void method() {}
};

The templated CRTP factory derives from a base class that's friends with Base and thus has access to the private checker method; it also has access to the protected constructors in order to construct any of the derived classes.

class MakeBase {
protected:
   static void check(Base * b) { b->check(); }
};

The factory class can issue a readable compile-time error message if you inadvertently use it on a class not derived from Base:

template <class C> class Make : public C, MakeBase {
public:
   template <typename... Args> Make(Args&&... args) : C(std::forward<Args>(args)...) {
      static_assert(std::is_base_of<Base, C>::value,
                    "Make requires a class derived from Base");
      check(this);
   }
};

The derived classes must have a protected constructor:

class Derived : public Base {
   int a;
protected:
   Derived(int a) : a(a) { cout << "Derived() " << endl; }
   void method() override { cout << ">" << a << "<" << endl; }
};

int main()
{
   Make<Derived> d(3);
}

Output:

Base()
Derived() 
check()
>3<
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
-1

Seems you want this, or need more details.

class B 
{
    void templateMethod()
    {
        foo();
        bar();
    }

    virtual void foo() = 0;
    virtual void bar() = 0;
};   

class D : public B
{
public:
    D()
    {
        templateMethod();           
    }

    virtual void foo()
    {
        cout << "D::foo()";
    }

    virtual void bar()
    {
        cout << "D::bar()";
    }
};
-1

I would write a constructor for the base class which calls the function that you want and inherit the constructor

class Base {
public:
    Base() {someFunc(); /* Rest of instructions the constructor should handle */}

protected:
    // Protected stuff

private:
    void someFunc() {/* Function Body */}
};


class Derived : Base {
public:
    Derived() : Base() {/* Your derived constructor */}

// ...
};

(This is of course only convenient if the base constructor handles general functionality of the derived classes)

General Grievance
  • 4,555
  • 31
  • 31
  • 45
  • Unfortunately, this doesn't address the situation in the original question where someFunc() is virtual or calls a virtual method. – Nigel Hawkins May 12 '23 at 13:51