-->

Must the entire class hierarchy be recompiled if a

2020-04-17 08:07发布

问题:

I am trying to learn how sensitive the vtable of a class is in C++ and, for that, I need to know whether recompilation of the entire class hierarchy (total, 3 header files) is necessary for the 3 change-scenarios I list below. First, here is my class hierarchy:

class A {
   public:
   virtual void method1() = 0;
   virtual void method2() = 0;
   virtual ~A() {} 
};

class B : public A {
    public:
    virtual void method1() {};
    virtual void method2() {};
    virtual ~B() {} 
};

class C : public A {
    public:
    virtual void method1() {};
    virtual void method2() {};
    virtual ~C() {} 
};

Here are my scenarios:

  1. A non-virtual method is added to the base class A:

    void method3() {};
    
  2. A virtual method with a body is added to the base class A:

    virtual void method3() {};
    
  3. A purely virtual method is added to the base class A:

    virtual void method3() = 0;
    

In scenario-1, no change to the vtable is made. Is it still required for B and C to be recompiled?

In scenario-2, will the vtable be reconstructed for base A, and consequently for B, and C?

I know scenario-3 will force classes B and C to provide implementations for the new method. So, the entire hierarchy must be recompiled.

回答1:

The C++ one-definition-rule makes it clear that definitions of entities in different translation units (ie: files) must all be identical if you're going to link them together. As such, if you change the definition of a class at all, public, private, virtual, non-virtual, whatever, all translation units that use that definition have to be looking at the new class definition. And that will require recompiling it.

Failure to do this is il-formed, but no diagnostic (linker-error) is required. So your project may appear to link just fine. Indeed, it may actually work in some cases. But there's nothing which guarantees in which cases they will work and in which cases they will not.



回答2:

Regardless the missing virtualdestructor of A you have an interface here:

class A {
   public:
   virtual void method1() = 0;
   virtual void method2() = 0;
   virtual ~A() {} // This is needed
};

An interface is a contract that shouldn't be broken.

If you want to extend interfaces without the need to recompile the complete codebase, you can use inheritance and dynamic polymorphism checks:

class AEx : public A {
public:
   virtual void method3() = 0;
   virtual ~AEx() {}
}; 

In functions which handle the extensions you can do

void foo(A* pA) {
    AEx pAEx = dynamic_cast<AEx*>(pa);
    if(pAEx) {
       pAEx->method3();
    }
}