开发者

Why virtual function can't be unimplemented when allocated with 'new'?

struct A
{
  virtual void foo();  // unused and unimplemented
  virtual void bar () {}
};

int main ()
{
  A obj;        // ok
  obj.bar();  // <-- added this edition
  A* pm = (A*)m开发者_运维知识库alloc(sizeof(A)); // ok
  A* pn = new A; // linker error
}

For objects on stack it works fine. But for allocation on heap with new (not malloc), it gives linker error:

undefined reference to `vtable for A'


Because malloc does not call (or attempt to call in this case) A's constructor, whereas new does.

This code compiles and notes where linker errors occur with GCC:

#include <cstdlib>

struct A
{
  virtual void foo();  // unused and unimplemented
  virtual void bar () {}
};

int main ()
{
  A obj;        // linker error
  A* pm = (A*) malloc(sizeof(A)); // ok
  A* pn = new A; // linker error
}


Firstly, this code is not compilable, since in C++ void * cannot be implicitly converted to A *. An explicit cast is required.

Secondly, the example with malloc is completely irrelevant. malloc allocates raw memory, with has absolutely no relation to any specific types. In this case malloc knows noting about any A and it does not create an object of type A.

For this reasons the real example for this question should look as follows

struct A
{
  virtual void foo();  // unused and unimplemented
  virtual void bar () {}
};

int main ()
{
  A obj;        // ok
  A* pn = new A; // linker error
}

And the question is why the first declaration produces no liker error while the second one does.

From the formal point of view, your program is invalid because it violates formal requirements of C++ language (specifically ODR). In practice, both declarations could or should produce the same error, since in both cases the object formally requires a pointer to VMT. In this case VMT cannot be created, since some functions are undefined. However, the first declaration simply slips through just because the compiler was able to optimize-out all references to VMT for the first declaration (and not for the second). It is also quite possible that the compiler was able to optimize-out the whole obj object, since it is not referenced anywhere else.

In GCC (since you appear to be using GCC) it is easy to trigger the same error for the first declaration as well

struct A
{
  virtual void foo();  // unused and unimplemented
  virtual void bar () {}
};

int main ()
{
  A obj; // linker error
  A *p = &obj;
  p->bar(); 
}

The above code will produce the same linker error in GCC even though the undefined function foo is still not used in this code.

In other words, it is simply a matter of adding sufficient amount of code to make the compiler believe that the object's VMT is needed. The difference in behavior between the declarations has nothing to do with C++ language in this case. It is just an implementation issue specific to your compiler.


You cannot leave a virtual function unimplemented, even if it's 'unused' (because it's in fact used by the vtable). This is the bug in the code.

The bug manifests itself in this particular fashion because of a peculiar implementation of vtables in the compiler. You have left unimplemented the first virtual function. The compiler inserts the vtable whenever it sees the implementation of the first virtual function of the class. Since there isn't any, there's no vtable.

If you leave the second function unimplemented, the linker will complain about that specific function, not about the vtable.

[edit] Your compiler probably optimized-out a copy of A on the stack, that's the reason the linker didn't complain.

The malloc line doesn't actually reference an object of type A, that's why it doesn't create a linker problem. There's another problem with this line though: it should not compile. malloc returns void* which does not convert to other types of pointer without a cast.


The standard requires exactly one implementation of A::foo if A is instantiated anywhere in the program. Regardless of whether the instantiation is through the declaration of a local variable or through a new expression. However, no diagnostic is required if this rule is broken; if you provide no declaration, or if you provide two or more, it's simply undefined behavior. Anything the compiler does is "correct". In this case, what it probably happening is:

  • the reason the definition is required is because it is referenced in the vtable,
  • the constructor of A is inline, so the code which initializes the vptr (and triggers the instantiation of the vtable) is fully visible to the compiler,

  • since all uses of the object are visible to the compiler, it can see that the vptr is never used, so it simply suppresses it.

  • and with no vptr, no vtable need be generated, so there is no reference to the virtual function.

In sum, it depends on how the compiler optimizes; you might get an error for both the local declaration and the new expression, or for neither, or for one and not the other. And it might depend on the optimization options, or whatever. As far as C++ is concerned, it might depend on the phases of the moon, and instead of an error, you might simply get code which crashed when you ran it (but the scenarios I stated first are the most likely).


Unusedness is irrelevant. Define all virtual functions. It's as simple as that.

Your automatic storage duration object (what you've chosen to call an object "on the stack") is not used [polymorphically], so you get no diagnostic. That doesn't make it right.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜