call to pure virtual function from base class constructor
Asked Answered
L

8

55

I have a base class MyBase that contains a pure virtual function:

void PrintStartMessage() = 0

I want each derived class to call it in their constructor

then I put it in base class(MyBase) constructor

 class MyBase
 {
 public:

      virtual void PrintStartMessage() =0;
      MyBase()
      {
           PrintStartMessage();
      }

 };

 class Derived:public MyBase
 {     

 public:
      void  PrintStartMessage(){

      }
 };

void main()
 {
      Derived derived;
 }

but I get a linker error.

 this is error message : 

 1>------ Build started: Project: s1, Configuration: Debug Win32 ------
 1>Compiling...
 1>s1.cpp
 1>Linking...
 1>s1.obj : error LNK2019: unresolved external symbol "public: virtual void __thiscall MyBase::PrintStartMessage(void)" (?PrintStartMessage@MyBase@@UAEXXZ) referenced in function "public: __thiscall MyBase::MyBase(void)" (??0MyBase@@QAE@XZ)
 1>C:\Users\Shmuelian\Documents\Visual Studio 2008\Projects\s1\Debug\s1.exe : fatal error LNK1120: 1 unresolved externals
 1>s1 - 2 error(s), 0 warning(s)

I want force to all derived classes to...

A- implement it

B- call it in their constructor 

How I must do it?

Laster answered 25/12, 2011 at 15:10 Comment(14)
How is it that your error says something about class A but your classes are named Derived and MyBase? Post the actual code please.Trashy
It might help if you clean up your class names in the example you provide. Your main references class "p", which isn't provided, and the linker error references class "A".Grishilda
Can you show us the line where that error occurs, it doesn't seem to occur because of what you want!Hackberry
Why does an abstract class even have a constructor at all? You can't create an instance of it, because of the pure virtual method. This isn't valid code.Risk
@peachykeen What could you do with a class without a constructor? You couldn't construct it!Aspergillosis
@Aspergillosis You can derive from it. Classes with pure virtual methods cannot be constructed, at all; the pure virtual is used to force derived classes to implement those methods.Risk
@peachykeen "You can derive from it." Yes. But since you can't construct it, can't construct any instance of a derived class either.Aspergillosis
@Aspergillosis Sure you can. Only the class you are creating needs a constructor. Taken to an extreme, this is the entire basis of interfaces. Even wikipedia has a concise article on why it works.Risk
@peachykeen Of course not. Who told you this nonsense? You can't create a derived instance without a base class constructor.Aspergillosis
@Aspergillosis You most definitely can construct the derived classes, and then pass back the abstract class. In fact, interfaces can be so abstract as to need not even have their own vtables, but that gets a bit messy. If you're interested in the details, look it up (anything related to COM should mention it).Risk
@peachykeen "You most definitely can construct the derived classes," You cannot construct a derived class as your base class is lacking any constructor (by hypothesis). In order to construct a derived instance you need to construct a base instance first.Aspergillosis
@Aspergillosis If you are interested in the details of how this can be done, look it up. Otherwise, no need to continue spamming.Risk
let us continue this discussion in chatAspergillosis
@Risk You seem to have mistaken classes that have a default, possibly no-op constructor - and arrive at the false conclusion that therefore such classes have no constructor. Obviously, that's nonsense, as the object model of C++ requires all classes and bases to be constructed, notionally, even if no code actually needs to be emitted. If a trivial (abstract or not/members or not) class is declared with no user-defined constructor, it gets a compiler-generated default one. Said constructor probably doesn't have to do anything, but that certainly does not mean it doesn't exist. And s/con/de/gClardy
R
49

There are many articles that explain why you should never call virtual functions in constructor and destructor in C++. Take a look here and here for details what happens behind the scene during such calls.

In short, objects are constructed from the base up to the derived. So when you try to call a virtual function from the base class constructor, overriding from derived classes hasn't yet happened because the derived constructors haven't been called yet.

Recurvate answered 25/12, 2011 at 15:23 Comment(2)
What if the base constructor invokes a non-virtual function that invokes the virtual function?Depredate
@Depredate It doesn't matter which function invokes a virtual function.Aspergillosis
P
24

Trying to call a pure abstract method from a derived while that object is still being constructed is unsafe. It's like trying to fill gas into a car but that car is still on the assembly line and the gas tank hasn't been put in yet.

The closest you can get to doing something like that is to fully construct your object first and then calling the method after:

template <typename T>
T construct_and_print()
{
  T obj;
  obj.PrintStartMessage();

  return obj;
}

int main()
{
    Derived derived = construct_and_print<Derived>();
}
Pavo answered 25/12, 2011 at 15:50 Comment(5)
"It's like trying to fill gas into a car but that car is still on the assembly line and the gas tank hasn't been put in yet." Excellent!Aspergillosis
Personally, I expect to be able to be able to centralise a common initialising sequence using values configured by the subclass, a common pattern in other languages like Obj-C, Ruby, Python (dynamic I know)Brittenybrittingham
"I mean what the hell do you expect to happen?" Is this a serious question ? The memory is already allocated before the initialization list is executed and there is no obvious thing that indicates that the virtual function table is or is not initialized while in the constructor body. And actually it would make sense if it was already initialized in the constructor body. I don't see any reason why some methods could be called and some other methods could not at this point, espcially since a non virtual could call a pure virtual.Haifa
@Haifa "there is no obvious thing that indicates that the virtual function table is or is not initialized while in the constructor body." Yes, there is: the C++ Standard. Vtables are built up as each derived layer is constructed, full stop. Whether you think it makes sense isn't relevant!Clardy
Great! The thing about initialization after object creation actually reminded me of factory pattern that can be used in this case.Mcghee
S
14

You can't do it the way you imagine because you cannot call derived virtual functions from within the base class constructor—the object is not yet of the derived type. But you don't need to do this.

Calling PrintStartMessage after MyBase construction

Let's assume that you want to do something like this:

class MyBase {
public:
    virtual void PrintStartMessage() = 0;
    MyBase() {
        printf("Doing MyBase initialization...\n");
        PrintStartMessage(); // ⚠ UB: pure virtual function call ⚠
    }
};

class Derived : public MyBase {
public:
    virtual void PrintStartMessage() { printf("Starting Derived!\n"); }
};

That is, the desired output is:

Doing MyBase initialization...
Starting Derived!

But this is exactly what constructors are for! Just scrap the virtual function and make the constructor of Derived do the job:

class MyBase {
public:
    MyBase() { printf("Doing MyBase initialization...\n"); }
};

class Derived : public MyBase {
public:
    Derived() { printf("Starting Derived!\n"); }
};

The output is, well, what we would expect:

Doing MyBase initialization...
Starting Derived!

This doesn't enforce the derived classes to explicitly implement the PrintStartMessage functionality though. But on the other hand, think twice whether it is at all necessary, as they otherwise can always provide an empty implementation anyway.

Calling PrintStartMessage before MyBase construction

As said above, if you want to call PrintStartMessage before the Derived has been constructed, you cannot accomplish this because there is no yet a Derived object for PrintStartMessage to be called upon. It would make no sense to require PrintStartMessage to be a non-static member because it would have no access to any of the Derived data members.

A static function with factory function

Alternatively we can make it a static member like so:

class MyBase {
public:
    MyBase() {
        printf("Doing MyBase initialization...\n");
    }
};

class Derived : public MyBase {
public:
    static void PrintStartMessage() { printf("Derived specific message.\n"); }
};

A natural question arises of how it will be called?

There are two solution I can see: one is similar to that of @greatwolf, where you have to call it manually. But now, since it is a static member, you can call it before an instance of MyBase has been constructed:

template<class T>
T print_and_construct() {
    T::PrintStartMessage();
    return T();
}

int main() {
    Derived derived = print_and_construct<Derived>();
}

The output will be

Derived specific message.
Doing MyBase initialization...

This approach does force all derived classes to implement PrintStartMessage. Unfortunately it's only true when we construct them with our factory function... which is a huge downside of this solution.

The second solution is to resort to the Curiously Recurring Template Pattern (CRTP). By telling MyBase the complete object type at compile time it can do the call from within the constructor:

template<class T>
class MyBase {
public:
    MyBase() {
        T::PrintStartMessage();
        printf("Doing MyBase initialization...\n");
    }
};

class Derived : public MyBase<Derived> {
public:
    static void PrintStartMessage() { printf("Derived specific message.\n"); }
};

The output is as expected, without the need of using a dedicated factory function.

Accessing MyBase from within PrintStartMessage with CRTP

While MyBase is being executed, its already OK to access its members. We can make PrintStartMessage be able to access the MyBase that has called it:

template<class T>
class MyBase {
public:
    MyBase() {
        T::PrintStartMessage(this);
        printf("Doing MyBase initialization...\n");
    }
};

class Derived : public MyBase<Derived> {
public:
    static void PrintStartMessage(MyBase<Derived> *p) {
        // We can access p here
        printf("Derived specific message.\n");
    }
};

The following is also valid and very frequently used, albeit a bit dangerous:

template<class T>
class MyBase {
public:
    MyBase() {
        static_cast<T*>(this)->PrintStartMessage();
        printf("Doing MyBase initialization...\n");
    }
};

class Derived : public MyBase<Derived> {
public:
    void PrintStartMessage() {
        // We can access *this member functions here, but only those from MyBase
        // or those of Derived who follow this same restriction. I.e. no
        // Derived data members access as they have not yet been constructed.
        printf("Derived specific message.\n");
    }
};

No templates solution—redesign

Yet another option is to redesign your code a little. IMO this one is actually the preferred solution if you absolutely have to call an overridden PrintStartMessage from within MyBase construction.

This proposal is to separate Derived from MyBase, as follows:

class ICanPrintStartMessage {
public:
    virtual ~ICanPrintStartMessage() {}
    virtual void PrintStartMessage() = 0;
};

class MyBase {
public:
    MyBase(ICanPrintStartMessage *p) : _p(p) {
        _p->PrintStartMessage();
        printf("Doing MyBase initialization...\n");
    }

    ICanPrintStartMessage *_p;
};

class Derived : public ICanPrintStartMessage {
public:
    virtual void PrintStartMessage() { printf("Starting Derived!!!\n"); }
};

You initialize MyBase as follows:

int main() {
    Derived d;
    MyBase b(&d);
}
Sarong answered 25/12, 2011 at 16:11 Comment(0)
A
6

You shouldn't call a virtual function in a constructor. Period. You'll have to find some workaround, like making PrintStartMessage non-virtual and putting the call explicitly in every constructor.

Archidiaconal answered 25/12, 2011 at 15:16 Comment(6)
I want froce to all Derived class impelment it,and call to it in counstrucetor How I can do it?Laster
@herzlshemuelian Like he said: not!Hare
Just to make it clearer, One can call a virtual functions from constructor or destructor as well, Just that it will not result in calling the derived class versions of the functions as one would expect. The this in constructor and destructor is always of the type of the class whose constructor or destructor is being called and hence the dynamic dispatch results in calling of the Base class versions of the overidden functions.Smoulder
@Als Calling a pure virtual function in constructor is undefined behavior.Annam
@Als If it is called from the derived class, and the derived class implements it without declaring it with =0, then it is not pure virtual in the derived class. And as long as the function is declared with =0, it is pure virtual, and calling it from the constructor is undefined behavior whether it has a definition or not.Annam
@fefe: Yes you are correct, C++03 10.4/6 states "Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined."Smoulder
C
1

If PrintStartMessage() was not a pure virtual function but a normal virtual function, the compiler would not complain about it. However you would still have to figure out why the derived version of PrintStartMessage() is not being called.

Since the derived class calls the base class's constructor before its own constructor, the derived class behaves like the base class and therefore calls the base class's function.

Compact answered 27/12, 2012 at 14:0 Comment(1)
This is all true and well written, but because it opens by switching the focus to non-pure virtual methods, it doesn't answer the given question and didn't really need to be posted a year later.Clardy
T
0

I know this is an old question, but I came across the same question while working on my program.

If your goal is to reduce code duplication by having the Base class handle the shared initialization code while requiring the Derived classes to specify the code unique to them in a pure virtual method, this is what I decided on.

#include <iostream>

class MyBase
{
public:
    virtual void UniqueCode() = 0;
    MyBase() {};
    void init(MyBase & other)
    {
      std::cout << "Shared Code before the unique code" << std::endl;
      other.UniqueCode();
      std::cout << "Shared Code after the unique code" << std::endl << std::endl;
    }
};

class FirstDerived : public MyBase
{
public:
    FirstDerived() : MyBase() { init(*this); };
    void  UniqueCode()
    {
      std::cout << "Code Unique to First Derived Class" << std::endl;
    }
private:
    using MyBase::init;
};

class SecondDerived : public MyBase
{
public:
    SecondDerived() : MyBase() { init(*this); };
    void  UniqueCode()
    {
      std::cout << "Code Unique to Second Derived Class" << std::endl;
    }
private:
    using MyBase::init;
};

int main()
{
    FirstDerived first;
    SecondDerived second;
}

The output is:

 Shared Code before the unique code
 Code Unique to First Derived Class
 Shared Code after the unique code

 Shared Code before the unique code
 Code Unique to Second Derived Class
 Shared Code after the unique code
Tapdance answered 5/10, 2018 at 23:47 Comment(2)
Wrong solution. If you derive ThirdDerived from FirstDerived class, then FirstDerived::init will be called instead of ThirdDerived::init.Grindle
@Grindle For the application I was working on I only needed a depth of 1. It's only a "wrong solution" if your use case requires more.Tapdance
G
0

Facing the same problem, I imaginated a (not perfect) solution. The idea is to provide a certificate to the base class that the pure virtual init function will be called after the construction.

class A
{
  private:
    static const int checkValue;
  public:
    A(int certificate);
    A(const A& a);
    virtual ~A();
    virtual void init() = 0;
  public:
    template <typename T> static T create();
    template <typeneme T> static T* create_p();
    template <typename T, typename U1> static T create(const U1& u1);
    template <typename T, typename U1> static T* create_p(const U1& u1);
    //... all the required possibilities can be generated by prepro loops
};

const int A::checkValue = 159736482; // or any random value

A::A(int certificate)
{
  assert(certificate == A::checkValue);
}

A::A(const A& a)
{}

A::~A()
{}

template <typename T>
T A::create()
{
  T t(A::checkValue);
  t.init();
  return t;
}

template <typename T>
T* A::create_p()
{
  T* t = new T(A::checkValue);
  t->init();
  return t;
}

template <typename T, typename U1>
T A::create(const U1& u1)
{
  T t(A::checkValue, u1);
  t.init();
  return t;
}

template <typename T, typename U1>
T* A::create_p(const U1& u1)
{
  T* t = new T(A::checkValue, u1);
  t->init();
  return t;
}

class B : public A
{
  public:
    B(int certificate);
    B(const B& b);
    virtual ~B();
    virtual void init();
};

B::B(int certificate) :
  A(certificate)
{}

B::B(const B& b) :
  A(b)
{}

B::~B()
{}

void B::init()
{
  std::cout << "call B::init()" << std::endl;
}

class C : public A
{
  public:
    C(int certificate, double x);
    C(const C& c);
    virtual ~C();
    virtual void init();
  private:
    double x_;
};

C::C(int certificate, double x) :
  A(certificate)
  x_(x)
{}

C::C(const C& c) :
  A(c)
  x_(c.x_)
{}

C::~C()
{}

void C::init()
{
  std::cout << "call C::init()" << std::endl;
}

Then, the user of the class can't construct an instance without giving the certificate, but the certificate can only be produced by the creation functions:

B b = create<B>(); // B::init is called
C c = create<C,double>(3.1415926535); // C::init is called

Moreover, the user can't create new classes inheriting from A B or C without implementing the certificate transmission in the constructor. Then, the base class A has the warranty that init will be called after construction.

Grindle answered 3/6, 2020 at 5:51 Comment(0)
D
-1

I can offer a work around / "companion" to your abstract base class using MACROS rather than templates, or staying purely within the "natural" constraints of the language.

Create a base class with an init function e.g.:

class BaseClass
{
public:
    BaseClass(){}
    virtual ~BaseClass(){}
    virtual void virtualInit( const int i=0 )=0;
};

Then, add a macro for a constructor. Note there is no reason to not add multiple constructor definitions here, or have multiple macros to choose from.

#define BASECLASS_INT_CONSTRUCTOR( clazz ) \
    clazz( const int i ) \
    { \
        virtualInit( i ); \
    } 

Finally, add the macro to your derivation:

class DervivedClass : public BaseClass
{
public:
    DervivedClass();
    BASECLASS_INT_CONSTRUCTOR( DervivedClass )
    virtual ~DervivedClass();

    void virtualInit( const int i=0 )
    {
        x_=i;
    }

    int x_;
};
Dragonnade answered 15/2, 2020 at 0:50 Comment(2)
If you create another class Derived2 inheriting from DerivedClass you will have a bad surprise: DerivedClass::virtualInit will be called, but not Derived2::virtualInit.Grindle
Yeah... You just have to include the macro again for DerivedClass2. This isn't a "fool proof" solution, and not good for use in a public library, or whatever, but it's a viable option for private implementations. Macros, in general, have always tended to be like that.Dragonnade

© 2022 - 2024 — McMap. All rights reserved.