Solved

Pure virtual function linker error

Posted on 2006-07-06
22
594 Views
Last Modified: 2008-02-01

need clarification why linker error

when i compiled the above code i am getting linker error in vc6 (lnk 2001) when i call PVM directly in ctor .
but where as PVM called via interface in base class ctor program crashes.

when u call PVM in base class constructor then u need to provide implementation to solve linkere error.

My question is : why linker error when pure virtual function called directly in ctor ? ctor-->pvm
 why it did not behaved as called via interface ? ( no linker error ) ctor-->interface-->PVM


class Base
{
public:
     Base();
     virtual ~Base();
     void init();
     virtual void func()=0;
     
};

class Derived :public Base
{
public:
     Derived();
     virtual ~Derived();
     virtual void func();
};

Derived d;

// this crashes ctor is calling interface which in turn calls PVM . crashes .
Base::Base() { init(); }
void Base::init(){func(); }

// Gives linker error . bit confused
 Base::Base(){ func(); }

i knew object is in imatture state why different errors?
0
Comment
Question by:havman56
  • 7
  • 6
  • 5
  • +2
22 Comments
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> My question is : why linker error when pure virtual function called directly in ctor ?

The object isn't fully created when in the baseclass constructor. So, you can't call virtual functions there.

VC6 compiler should check this but it has some lacks. So, it is the linker which finds out that it would need a baseclass implementation of the function at this time.

Regards, Alex
0
 
LVL 53

Expert Comment

by:Infinity08
Comment Utility
And the reason it doesn't give a linker error for the other scenario (func() called from init()), is because that could be a valid scenario. When init() is called from the constructor as is the case here, it will crash of course. But init() called after the constructor doesn't pose a problem :

class Base
{
public:
     Base();
     virtual ~Base();
     void init();
     virtual void func()=0;
     
};

class Derived :public Base
{
public:
     Derived();
     virtual ~Derived();
     virtual void func();
};

// this crashes ctor is calling interface which in turn calls PVM . crashes .
Base::Base() { }
void Base::init(){func(); }

Derived d;
d.init();

0
 
LVL 53

Expert Comment

by:Infinity08
Comment Utility
Or in other words : the linker only checks 1 level of function calls, ie. it only checks :

constructor -> init()
init() -> func()

which are both ok !

It doesn't see that :

constructor -> init() -> func()

which causes the crash
0
 
LVL 4

Author Comment

by:havman56
Comment Utility
yes alex, i agree whatever u said . which i aldreay knew.

why linker error not reported for ctor-->init()-->func() ? rather it executes and gets crashed.

i hope something getting screwed somewhere .

infinity : i dont feel linker checks only one level of function calls .

any more thoughts ?
0
 
LVL 17

Expert Comment

by:rstaveley
Comment Utility
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> why linker error not reported for ctor-->init()-->func()

The virtual function table is made for any function (constructor is a function as well) separately. In the constructor the linker 'knows' that it would need Base::func cause virtual links are not available at that time. In case of calling init there is no need for the linker to add virtual links cause init isn't a virtual function. In function Base::init the linker simply adds links for all virtual overloads of func. It doesn't check the calls of init cause that's a compiler job. That virtual links only would fail if there is *no* overload of func at all.  
0
 
LVL 53

Expert Comment

by:Infinity08
Comment Utility
>> infinity : i dont feel linker checks only one level of function calls .
nonetheless, it's true :)

the rule says : you can't call a pure virtual function from a constructor ... if that's detected, the linker will complain.
But in this case, you're calling a normal method (init()) from the constructor which is not a problem at all.

From init(), you're calling a pure virtual method (func()), which is also not a problem, because it's not the constructor.

The linker doesn't see that the constructor ultimately calls func() via init(), so it doesn't complain. But since this IS an error (albeit not caught by the linker), the program will crash.
0
 
LVL 17

Expert Comment

by:rstaveley
Comment Utility
> In the constructor the linker 'knows' that it would need Base::func cause virtual links are not available at that time.

Yes, and [to elaborate on what Alex said] it is equivalent to making the static call:

    Base::Base() {Base::func();}

That's why you get the linker error rather than the compiler error, because it is looking for a default pure virtual function implementation. Default pure virtual functions can normally only be called statically, but the Ctor knows that the virtual method table is not yet set up for a dynamic call and it therefore silently assumes you are looking for a static call. I reckon the compiler ought to warn you when it does this, and you really ought to have to put the Base:: prefix explicitly on the call to state your static intentions. [If I ruled the world.]
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> albeit not caught by the linker

I still think a good compiler could check it. At least all happens in one class.

>>>> looking for a default pure virtual function

good point. If adding a default pure virtual function there is no linker error in case of calling func in the constructor.

That might be a reason why the compiler didn't/couldn't complain. You can add a default implementation inline and/or in any cpp you like. So, the compiler cannot know whether there is a default implementaion or not (and the linker doesn't care). Nevertheless, I think the compiler could give a warning if it doesn't find a inline implementation.

Regards, Alex
0
 
LVL 53

Expert Comment

by:Infinity08
Comment Utility
>> I still think a good compiler could check it. At least all happens in one class.
While it's at that, it can also check whether there are no memory leaks, check for possible thread deadlocks, ... You'd get a bloated compiler very fast.
I don't think a compiler should do more than syntax checking, and as this is not a syntax issue ...
0
 
LVL 17

Expert Comment

by:rstaveley
Comment Utility
> I don't think a compiler should do more than syntax checking

Warnings have dug me out of many a mess.
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 53

Expert Comment

by:Infinity08
Comment Utility
>> Warnings have dug me out of many a mess.
Point taken ... although you can only easily check for things that follow the compiler's logic, like using an uninitialized variable. In this case, the compiler would have to understand the logic of the code ... and most compilers work very straightforward, without any more intelligence than they need to have.
0
 
LVL 4

Author Comment

by:havman56
Comment Utility
yes i accept all your comments thanks for your comments .

I feel
1. whenever pure virtual function is in class , there is no vtable link for PVM unless derived object matures.
2. whenever u call purevirtual function which is a dynamic call in ctor() it is looking for vtable link . which is not present so linker complains it.
3. but whenever called via init ctor->init()-->func() . since derived class has a vtable link of it's own derived::func()  compiler assumes init will call func() of derived .
4. but derived object is not matured hence program crashes for init-->func

expecting some more thoughts .

BTW,bunches of thanks for all .

0
 
LVL 4

Author Comment

by:havman56
Comment Utility

one more observation

1. when ctor()-->func() linker looking for base::func() implementation
2. when ctor()-->init()-->func() linker looking for Derived::func() implementation

0
 
LVL 17

Accepted Solution

by:
rstaveley earned 500 total points
Comment Utility
I've attempted to explain this more than once in http:#17049402 and http:Q_21908405.html#17050692. I'm not doing too well. I'll try again.

Consider the following:
--------8<--------
#include <iostream>
using std::cout;

struct base {
        virtual void f() {
                cout << "base::f() called\n";
        }
};

struct derived : public base {
        void f() {
                cout << "derived::f() called\n";
        }
};

void test(base& b)
{
      b.f();            // Dynamic call via VMT
      b.base::f();      // Static call (not via VMT)
}

int main()
{
      base b;      // Using a base object
      test(b);      // Perform the test on the base object

      derived d;      // Using a derived object
      test(d);      // Perform the test on the derived object
}
--------8<--------

If you have a go with the above that you'll see that the unfashionable static call syntax results in a call to the base class's f() implementation. So, we see that a virtual function can be called statically. The default syntax assumes that you want to call it via the VMT - i.e. dynamically or polymorphically.

The default syntax in the constructor is an exception to this.

See if you can guess which f() is called by init() in the following (i.e. see if you can gues which f() gets called first):
--------8<--------
#include <iostream>
using std::cout;

struct base {
      void init() {
            f();
      }
        virtual void f() {
                cout << "base::f() called\n";
        }
};

struct derived : public base {
      derived() {
            init();
            f();
            derived::f();
      }
        void f() {
                cout << "derived::f() called\n";
        }
};

struct morederived : public derived {
        void f() {
                cout << "morederived::f() called\n";
        }
};


int main()
{
      morederived m;
}
--------8<--------

If the VMT wasn't set up at all, you'd expect base::f() to be invoked via the VMT and if it was completely set up, you'd expect morederived::f(). The truth is that during the construction of derived (i.e. while you are in the derived constructor), the VMT is set up for derived, but hasn't get been realigned for morederived.

The second and third calls to f() in this are both static and equivalent and do not go via the VMT at all. The third is explicitly stated and the second is in my opinion correct but misleading. That's why I'd like to get a warning from the compiler. Unlike Infinity08 (I'm teasing him now - I should get a flaming for this :-)), I'm not always alert enough to remember that a static call is going to occur and that the second invocation is equivalent to the third. Calls to virtual functions directly made from constructors are statically invoked. Weird but true.
0
 
LVL 53

Expert Comment

by:Infinity08
Comment Utility
Nice explanation, rstaveley !

>> Unlike Infinity08 (I'm teasing him now - I should get a flaming for this :-))
lol ... you'd have to go a LOOOT further to get a flaming from me heh

>> I'm not always alert enough to ...
Neither am I, but the point I was trying to make is that it's not the compiler's job to help you with this, as its architecture generally doesn't allow to do this easily. Adding an extra module (to the IDE) that does these kind of checks for the programmer is a better approach imo. So, you have an IDE with compiler, linker, debugger, and all other handy modules to make the programmer's life easier.
I might have confused people when using the word compiler for the actual compiler ... as generally, with the word compiler, people seem to refer to the IDE. Oh well, ...

Gotten a bit off track here lol ... back to the good old virtual functions :)
0
 
LVL 8

Expert Comment

by:kaliyugkaarjun
Comment Utility
class A {
public:
virtual ~A() = 0 {} // wrong as well
};

It would be a syntax error. The definition has to be put at the namespace
level.

if you did declare ~A() pure, you can't define it in the class.

Consider the following:

class Base {
public:
virtual void vfunc();
void nvfunc() {
vfunc();
};
};

class Derived : public Base {
public:
void vfunc();
Derived() {
nvfunc();
}
};

class MoreDerived : public Derived {
void vfunc();
};

MoreDerived foo;

While the object is being created and the Derived function is being
invoked, the dynmaic type is set to Derived. Derived's constructor
calls Based::nvfunc which calls Derived::vfunc (not base::vfunc
or MoreDerived::vfunc).


If Base::vfunc was declared pure virtual, the behavior here is undefined.
0
 
LVL 17

Expert Comment

by:rstaveley
Comment Utility
kaliyugkaarjun, your class A is a good example of where VC 7.1 shows itself to be less standards-compliant than GCC 4.1 (actually eveb GCC 2.95 rejects this but without such a helpful error message).

> If Base::vfunc was declared pure virtual, the behavior here is undefined.

It would be counter-intuitive for the base class's PVF to make any difference. With a minor modification to my previous code, I can reproduce a test case for that. The following gets consistent results on my compilers. [Yes, I know that's not a great way to assert what is and isn't "defined" in the standard.]

Are you basing what you say on the C++ standard? Or have I misread what you said and you mean that it would be undefined if Derived::f() was also pure?

--------8<--------
#include <iostream>
using std::cout;

struct base {
      void init() {
            f();
      }
      virtual void f() = 0;      // PVF
};

void base::f() { // PVF default definition
      cout << "base::f() called\n";
}

struct derived : public base {
      derived() {
            init();
            f();
            derived::f();
            base::f();
      }
      void f() { // Not pure
            cout << "derived::f() called\n";
      }
};

struct morederived : public derived {
      void f() {
            cout << "morederived::f() called\n";
      }
};


int main()
{
      morederived m;
}
--------8<--------
0
 
LVL 4

Author Comment

by:havman56
Comment Utility
Thanks for everyone who participated in this thread
0
 
LVL 4

Author Comment

by:havman56
Comment Utility

infinity please accept my points for answering in this thread at

http://www.experts-exchange.com/Programming/Q_21913401.html

0
 
LVL 53

Expert Comment

by:Infinity08
Comment Utility
If you want to award me points, you can ask to re-open the question in community support :

http://www.experts-exchange.com/Community_Support/

just post a 0-point question there.

I can't accept the points in your other post though, because that would violate the rules of this site (you can't award more than 500 points for 1 question). For that post, you can ask to get your points refunded.

In any case, I'm glad if i have been of assistance to you.
0
 
LVL 17

Expert Comment

by:rstaveley
Comment Utility
Infinity08 is right, you should ask CS to reopen the question so you can do a split. It was a very good question, by the way :-)
0

Featured Post

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

Suggested Solutions

Templates For Beginners Or How To Encourage The Compiler To Work For You Introduction This tutorial is targeted at the reader who is, perhaps, familiar with the basics of C++ but would prefer a little slower introduction to the more ad…
  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now