Suddenly, you can call a virtual function of a derived class from the ctor base class

Can someone explain this unexpected behavior?

Room

I created a Thread class containing the member std::thread variable. Thread ctor creates a std::thread element that provides a pointer to a static function that calls a pure virtual function (which will be implemented by the base classes).

The code

 #include <iostream> #include <thread> #include <chrono> namespace { class Thread { public: Thread() : mThread(ThreadStart, this) { std::cout << __PRETTY_FUNCTION__ << std::endl; // This line commented later in the question. } virtual ~Thread() { } static void ThreadStart(void* pObj) { ((Thread*)pObj)->Run(); } void join() { mThread.join(); } virtual void Run() = 0; protected: std::thread mThread; }; class Verbose { public: Verbose(int i) { std::cout << __PRETTY_FUNCTION__ << ": " << i << std::endl; } ~Verbose() { } }; class A : public Thread { public: A(int i) : Thread() , mV(i) { } virtual ~A() { } virtual void Run() { for (unsigned i = 0; i < 5; ++i) { std::cout << __PRETTY_FUNCTION__ << ": " << i << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } } protected: Verbose mV; }; } int main(int argc, char* argv[]) { A a(42); a.join(); return 0; } 

Problem

As you already noticed, there is a subtle error here: Thread::ThreadStart(...) is called from the Thread ctor context, so calling a pure / virtual function will not call the implementation of the derived class. This is confirmed by a runtime error:

 pure virtual method called terminate called without an active exception Aborted 

However, there is an unexpected runtime behavior if I remove the call to std::cout in Thread ctor:

 virtual void {anonymous}::A::Run(){anonymous}::Verbose::Verbose(int): : 042 virtual void {anonymous}::A::Run(): 1 virtual void {anonymous}::A::Run(): 2 virtual void {anonymous}::A::Run(): 3 virtual void {anonymous}::A::Run(): 4 

those. removing the call to std::cout in Thread ctor seems to have the effect of invoking the derived class of the "pure / virtual function from the context of the constructor of the base class"! This is not consistent with previous training and experience.

Create an environment in Cygwin x64 on Windows 10. gcc Version:

 g++ (GCC) 5.4.0 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 

I am puzzled by this observation and grieving with curiosity about what is happening. Can anyone shed some light?

+6
source share
1 answer

The behavior of this program is undefined, due to the state of the race.

But, if you want to talk about it, try it.

For A build, here is what happens:

  • mThread initialized. OS plans to launch it at some point in the future.
  • std::cout << __PRETTY_FUNCTION__ << std::endl; - This is a rather slow operation from the point of view of the program.

  • A starts the constructor - initializes it to vtable (this is not required by the standard, but as far as I know, all implementations do this).

    If this happens before mThread , you get the behavior you observe. Otherwise, you will receive a clean virtual call.

Since these operations are in no way sequenced, the behavior is undefined.

You may notice that you removed a rather slow operation from your base constructor, thereby greatly accelerating the initialization of your derivative - and its vtable. Say, before the OS actually planned to launch mThread . However, this does not fix the problem, just made it less likely.

If you change your example a bit, you will notice that removing the I / O code makes it difficult to find a race, but nothing has been fixed.

 virtual void Run() { for (unsigned i = 0; i < 1; ++i) { std::cout << __PRETTY_FUNCTION__ << ": " << i << std::endl; // std::this_thread::sleep_for(std::chrono::seconds(1)); } } 

the main:

 for(int i = 0; i < 10000; ++i){ A a(42); a.join(); } 

demo

+9
source

Source: https://habr.com/ru/post/1011840/


All Articles