Is it possible to break code by adding a new virtual function in the base class?

怎甘沉沦 提交于 2019-12-23 20:32:58

问题


Is it possible to have the observed behavior of a program changed by simply adding a new virtual function to a base class? I mean that no other change must be made to the code.


回答1:


#include <stdlib.h>

struct A {
#if ADD_TO_BASE
  virtual void foo() { }
#endif
};

struct B : A {
  void foo() { }
};

struct C : B {
  void foo() { abort(); }
};

int main() {
  C c;
  B& b = c;
  b.foo();
}

Without the virtual function the base class b.foo() is a non-virtual call to B::foo():

$ g++ virt.cc
$ ./a.out

With the virtual in the base class it is a virtual call to C::foo():

$ g++ virt.cc -DADD_TO_BASE
$ ./a.out
Aborted (core dumped)

You can also get nasty undefined behaviour due to binary incompatibility, because adding a virtual function to a non-polymorphic base class changes its size and layout (requiring all other translation units that use it to be recompiled).

Adding a new virtual function to an already polymorphic base class changes the layout of the vtable, either adding a new entry at the end, or changing the position of other functions if added in the middle (and even if added at the end of the base vtable, that's in the middle of the vtable for any derived classes which add new virtual functions). That means that already compiled code that uses the vtable might end up calling the wrong function, because it uses the wrong slot in the vtable.

The binary incompatibilities can be fixed by recompiling all the relevant code, but silent changes in behaviour like the example at the top can't be fixed simply by recompiling.




回答2:


The following program prints OK. Uncomment the virtual function in B and it will start printing CRASH!.

#include <iostream>

struct B
{
    //virtual void bar() {}
};

struct D : B
{
    void foo() { bar(); }
    void bar() { std::cout << "OK" << std::endl; }
};

struct DD : D
{
    void bar() { std::cout << "CRASH!" << std::endl; }
};

int main()
{
    DD d;
    d.foo();
    return 0;
}

The problem is that after a virtual function B::bar() is introduced the binding of the call to bar() in D::foo() changes from static to dynamic.




回答3:


Binary incompatibility.

If you have an externally loadable module (i.e a DLL) then which uses the old definition of the base class you will have problems. Or if the loader program have the old definition and the DLL have the new it's the same problem. This is also a problem if you for some reason save objects in files using raw binary copying (not any kind of serialization).

This have nothing to do with with the C++ specification of virtual functions, but how most compilers implement them.

Generally speaking, if the "interface" of a class changes (base class or not) then you should recompile everything which uses that class.




回答4:


When the API is changed in a backwards incompatible manner, the code that depends on the earlier version of the API is no longer guaranteed to work.

All derived classes depend on the API of their base classes.

Addition of a virtual function is a backwards incompatible change. Leon's answer shows a fine example of how the API breakage can manifest itself.

Therefore yes, addition of a virtual function can break the program, unless the dependant parts are fixed to work with the new API. This means that whenever a virtual function is added, one should inspect all derived classes, and make sure that the meaning of their respective API has not been changed by the addition.



来源:https://stackoverflow.com/questions/38657534/is-it-possible-to-break-code-by-adding-a-new-virtual-function-in-the-base-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!