`__declspec(novtable)` 没用吗?

Does `__declspec(novtable)` no use?

我们知道,novtable表示不要为纯抽象class创建虚拟table。但是当我 运行 代码失败时,出了点问题:

#include <iostream>
using namespace std;

struct A{
    virtual void fun() = 0;
};

struct __declspec(novtable) B{
    virtual void fun() = 0;
};

struct C{
    void fun(){}
};

struct _declspec(novtable) D : public A {};

int main(){
    cout<<sizeof(int*)<<endl;  //4
    cout<<sizeof(A)<<endl;  //4
    cout<<sizeof(B)<<endl;  //4
    cout<<sizeof(C)<<endl;  //1
    cout<<sizeof(D)<<endl;  //4
    return 0;
}

AB大小一样,是不是说novtable没用了?

ps: 用vs2019编译

docs.microsoft.com 读作:

The __declspec(novtable) stops the compiler from generating code to initialize the vfptr in the constructor(s) and destructor of the class. In many cases, this removes the only references to the vtable that are associated with the class and, thus, the linker will remove it.

换句话说,vfptr仍将作为数据成员存在,但其值不会被初始化。例如,如果您设法调用一个纯虚函数,而不是像 "A pure virtual function call" 这样的消息,您将得到一个分段错误。

The size of A and B are same, does that means novtable no use?

__declspec(novtable)的目的不是减小对象大小,而是去掉一些你不需要的初始化代码和vtable你也不需要的。

class B 没有 vtable。这并不意味着 B 实例的对象中没有 vtable pointer。需要 vtable 指针以便

例如,如果您创建 BDerived 的实例:

struct BDerived : public B {
    void fun() {}
};

BDerived bd;
B* pb = &bd;
pb->fun();

pb指向一个B子对象,其中包含一个vtable指针,指向BDerived的vtable。当你调用 pb->fun() 时,程序会查看 pb 的 vtable 指针,跟随它到 BDerived 的 vtable,然后在那个 vtable 中查找 BDerived' s 执行 fun.


换句话说,编译器将代码翻译成这样:

vtable A_vtable = {NULL};
struct A {
    vtable *vtable_ptr;
};

// No vtable, but still a vtable pointer
struct B {
    vtable *vtable_ptr;
};

void BDerived_fun() {};
vtable BDerived_vtable = {&BDerived_fun};
struct BDerived : public B {
};

BDerived bd; bd.vtable_ptr = &BDerived_vtable;
B* pb = &bd;
(pb->vtable_ptr.fun)(pb);