64,281
社区成员
发帖
与我相关
我的任务
分享
class __declspec(novtable) A
{
public:
A(){}
virtual void Test(){}
virtual void Test2(){}
};
class __declspec(novtable) B : public A
{
public:
B(){}
virtual void Test(){}
virtual void Test3(){Test2();}
};
int main()
{
B b;
b.Test();
b.Test3();
getchar();
return 0;
}
This is a __declspec extended attribute.
This form of __declspec can be applied to any class declaration, but should only be applied to pure interface classes, that is, classes that will never be instantiated on their own. The __declspec 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. Using this form of __declspec can result in a significant reduction in code size.
If you attempt to instantiate a class marked with novtable and then access a class member, you will receive an access violation (AV).
class __declspec(novtable) A
{
public:
A(){}
virtual void Test(){}
virtual void Test2(){Test();}
};
A a;
a.Test(); //(1)OK
a.Test2();//(2)Error
A &a1 = a;
a1.Test(); (3)//Error;
A *pA = &a;
pA->Test();(4)//Error;
4个调用只有(1)可以,那就是说后面3个调用都涉及到虚指针和虚表,因为有__declspec(novtable)修饰,虚表没有构建,虚指针没有初始化所以违规访问了。(3)和(4)还能理解下,(2)有点不太明白,难道在虚函数里再调用另一个虚函数一定会涉及到虚表/虚指针,我预期的是编译器会把这2个调用都当作nonstatic member function来处理,看来是我理解错了。
或者换个问题,对于一个虚函数,在什么情况下编译器会把它当作nonstatic member function一样来处理,什么时候会通过虚表虚指针来处理?
另多谢建议,不懂汇编,对C++理解也不够深,书是看了几遍,但依旧没完全看懂[/quote]
c++ 设计 virtual 的目的,就是为了实现多态,那引起多态发生也是有条件的:
指针和引用,这个可以帮助你理解(3)(4)。
(2)呢,其实也不难理解,回到virtual 的目的,是为了实现多态。假定当前有个类B继承了A,
同时实现了函数Test()。
然后有:
B b;
A *a = &b;
a->Test2(); //那么此时A::Test2()应该调用B::Test()
所以调用了虚函数的成员函数也应该使用虚指针调用,才可得出正确结果。
看增加了一个非虚函数Test3()的代码,虚函数对非虚函数的调用是可以的。
class __declspec(novtable) A
{
public:
A(){}
virtual void Test(){}
virtual void Test2(){/*Test*/Test3();}
void Test3() {}
};
int main()
{
A a;
a.Test(); //(1)OK
a.Test2();//(2)OK
return 0;
}
class __declspec(novtable) A
{
public:
A(){}
virtual void Test(){}
virtual void Test2(){Test();}
};
A a;
a.Test(); //(1)OK
a.Test2();//(2)Error
A &a1 = a;
a1.Test(); (3)//Error;
A *pA = &a;
pA->Test();(4)//Error;
4个调用只有(1)可以,那就是说后面3个调用都涉及到虚指针和虚表,因为有__declspec(novtable)修饰,虚表没有构建,虚指针没有初始化所以违规访问了。(3)和(4)还能理解下,(2)有点不太明白,难道在虚函数里再调用另一个虚函数一定会涉及到虚表/虚指针,我预期的是编译器会把这2个调用都当作nonstatic member function来处理,看来是我理解错了。
或者换个问题,对于一个虚函数,在什么情况下编译器会把它当作nonstatic member function一样来处理,什么时候会通过虚表虚指针来处理?
另多谢建议,不懂汇编,对C++理解也不够深,书是看了几遍,但依旧没完全看懂class /*__declspec(novtable)*/ A
{
public:
A(){}
virtual void Test(){}
virtual void Test2(){}
};
编译器会在该类的构造函数里面插入类似如下的伪代码:
A.vfptr = A.vtab;
当然也有个虚表生成:
void *A.vtab[] = {A::Test, A::Test2};
但当类声明成这样:
class __declspec(novtable) A
{
public:
A(){}
virtual void Test(){}
virtual void Test2(){}
};
A.vfptr = A.vtab; 这句代码就不会在生成,甚至在大多时候,Linker看到A.vtab没被引用,
认为是dead code,就会把它移调,以减少代码占用空间。
回到LZ的情况:
只要A,B任意一个类不使用novtable声明,B.vfptr都会有值,程序都能运行,但未必是你要的情况。
具体来说,
当 A 用 novtable 而 B 不用,B.vfptr = B.vfptr;
当 A 不用而 B 用 novtable,B.vfptr = A.vfptr;
当 A,B 都用 novtable, B.vfptr不被初始化。
以上分析,仅供参考,LZ可以自己验证。
小小建议,LZ 如果是初次看 C++ 对象模型剖析之类的书,建议动态查看类的反汇编代码,自己有了
心得,再去看书。这样就变成了验证,修谬,掌握了主动,印象深刻,较易记住。否则被动追书,将会
很吃力,也很容易忘记。