Hi,

I have always been a little confused about inline/non-inline virtual functions and got a little more confused when I read this in a book:

According to the book the code below will fail to link on many systems(it gave no errors on mine, g++ 4.4.1). The explanation is "Even though no Base or Derived objects are created the example will fail to link on many systems. The reason is that the only virtual function in class Derived is inline (synthesized dtor), so the compiler puts a static copy of Derived::~Derived() into the current source file.Since this static copy of Derived::~Derived() invokes Base::~Base() the linker will need a definition of Base::~Base()"

class Base{
public:
        virtual ~Base() throw();
};

class Derived: public Base{

};

int main(){}

Solution mentioned is to add a non-inline virtual function to derived class.

Can someone please explain what is happening here? I cannot understand why will this give a linker error, on some systems, with nothing being created at all. Also what is the problem with having only inline virtual functions in a class?

Recommended Answers

All 3 Replies

Let me try to take a crack at this, after googling it. Its gonna be a long post, so
I hope you are ready to read it.

So first things first you know what inline functions are right? Just to recap, when
you inline a function, you suggest to the compiler that it inline or substitute the
definition where the function call was.

So the next thing, you probably know what virtual function does. Virtual functions
enable polymorphism. When you call a derived class from a base class pointer/ref, it
calls the derived class function.

Ok so thats cleared up. Now lets get started towards the problem.

Here is the first question that needs to be answered, and the main problem
that underlies with inline virtual function, consider the following code :

struct B{
 inline void show(){ _showIt(); } //explicitly tell compiler to hint of inlining it
 void tell(){ _tellIt(); } //implicitly tell the compiler to hint of inlining this func
};

int main(){
 B b;
 b.show();
 b.tell();
}

So suppose a the compiler accepts your inline hint, and inline those function, then
that means b.show() and b.tell() would be substituted with its definition.

Ok now consider this code :

struct B{
 virtual int id(){ return 1; }
};
struct D: B{
 int id(){ return 2; }
};

int main(){
 B base;
 D derv;
 
 base.id();
 derv.id();
}

Now look at the above code. The type of base and derv are known statically at compile
time, thus, the compiler CAN but does not necessarily need to inline the function
call, ok?


Now the problem comes when polymorphism comes in :

struct B{
 virtual int id(){ return 0;}
};
struct D : B{
 int id(){ return 1; }
};

void show(const B* b){
 print( b->id() ); //partly pseudo
}
int main(){ 
 B *b;
 b = new B();
 show(b);
 delete b;

 b = new D();
 show(b);
 delete b;

}

Now in the above code, in the show function, the passed in parameter could be
of type B or D. In that example its trivial what it is during compile time, but
imagine it depending on the user's input, then the compiler can't know if it could
inline the function during runtime, because which version does it inline?

On most modern compiler, it just completely ignores this, and just uses the v-table.


Ok by now you get the main problem. Now finally lets get back to the problem you
presented.

1) Notice that you have not provided no definition for Base destructor. That means calling it will cause a linkage error right, for example :

struct B{
	virtual ~B();
};
int main(){
	B b; //linkage error, no definition for the destructor
}

Ok thats the first thing to notice, and ultimately that is whats gonna cause this
linkage error.

Now the next thing, since you declared it as virtual ~Base()throw()
that means all classes derived from Base, will call the Base destructor first, right?
Thats polymorphism. You should know that.

Now the next important thing, remember that when you provide no destructor/ctor/operator=/copy ctor, the compiler generates them for your class
automatically. Now since the derived class has no explicit destructor, it will create
a destructor for the Derived class, BUT since Derived is derived from base, which
has a virtual destructor, that means Derived class MUST call the base class destructor first before it destructs its own, right? This is because of polymorphism
which is a guarantee must!

This is where the problem comes in, since the derived class has no explicit destructor and the compiler generates one for you, that mean, in that case, the
compiler generates a static destructor for that class, which calls the Base class
destructor!. But wait, there is no definition for the base class destructor! Uh OH!
Thats where the linkage error comes in. The compiler is calling a function without
no definition, per say.

So I hope that helped. After goggling your question for a few minutes and reading
some *sources, thats what I got from your question, from what I understood.
----------------------------
* Googling source

commented: Excellent deep answer +3
commented: Good job +15

Thanks firstPerson, I can understand most of it but is the linker error going to appear even though we do not create any objects in the main? I mean do you expect an error in the code snippet i provided, exactly as it is? I know if I go to create an object it will be a problem because there is no definition for the base destructor but in the code snippet there are no objects getting created. Or do you think the author kind of assumed that?

Also from what i know the reason to have a virtual base destructor is so that the child class destructor gets invoked or else if we go to delete the base pointer pointing to the child class, only the base destructor will get called. Making it virtual ensures both the destructors are called and in the right order.

Although you created no object, the point is that, the compiler might create
a static instance of the base destructor, thats why there might be a linking error.
Whether the compiler does this or not, is depended on the compiler, but recent compiler
will not do this, but older ones might.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.