call to pure virtual function from base class cons

2019-01-06 14:28发布

问题:

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?

回答1:

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.



回答2:

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

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. I mean what the hell do you expect to happen?



回答3:

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

The desired execution trace would be:

Doing MyBase initialization...
Starting Derived!!!

But this is what constructors are for! Just scrap the virtual function and make the constructor of the derived to 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 are to call PrintStartMessage before the Derived has been constructor, 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

When MyBase is executing, 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);
}


回答4:

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.



回答5:

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.



回答6:

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