vfptr 中的 Visual C++ 方法以相反的顺序
Posted
技术标签:
【中文标题】vfptr 中的 Visual C++ 方法以相反的顺序【英文标题】:Visual C++ methods in vfptr in reverse order 【发布时间】:2014-02-27 04:32:05 【问题描述】:有没有办法控制 vfptr 中某些类方法的顺序?似乎 Visual C++ 2010 至少将方法指针放在声明顺序中,但重载方法除外。下面是示例代码:
enum ENUM
;
class CLASS
virtual void foo1() ;
virtual CLASS& __cdecl operator<<(const ENUM x) return *this; ;
virtual void foo2() ;
virtual CLASS& __cdecl operator<<(const char* x) return *this; ;
virtual CLASS& __cdecl operator<<(int x) return *this; ;
virtual void foo3() ;
virtual void foo1(int x) ;
;
int main()
CLASS c;
return 0;
上面的代码生成如下vfptr:
[0] 0x00e21091 CLASS::foo1(int) *
[1] 0x00e2100a CLASS::foo1(void) *
[2] 0x00e211ae CLASS::operator<<(int) *
[3] 0x00e21050 CLASS::operator<<(char const *) *
[4] 0x00e211db CLASS::operator<<(enum ENUM) *
[5] 0x00e2106e CLASS::foo2(void) *
[6] 0x00e21172 CLASS::foo3(void) *
因此,重载的方法首先被分组到这样的声明,然后再反转。将它们组合在一起不是问题,因为我可以轻松地将它们组合在头文件中。然而,相反的顺序是一个问题,因为不同的编译器显然不会这样做,这意味着它们会调用错误的方法。这与在应用程序和 DLL 之间传递对象指针有关。 首先,我希望 MSVC 对重载方法有一个优先顺序,所以我尝试按照 MSVC 在 vfptr 中的顺序声明方法,但随后它再次简单地反转它们,这是代码和结果:
class CLASS
virtual void foo1(int x) ;
virtual void foo1() ;
virtual CLASS& operator<<(int x) return *this; ;
virtual CLASS& operator<<(const char* x) return *this; ;
virtual CLASS& operator<<(const ENUM x) return *this; ;
virtual void foo2() ;
virtual void foo3() ;
;
结果:
[0] 0x0113100a CLASS::foo1(void) *
[1] 0x01131091 CLASS::foo1(int) *
[2] 0x011311db CLASS::operator<<(enum ENUM) *
[3] 0x01131050 CLASS::operator<<(char const *) *
[4] 0x011311ae CLASS::operator<<(int) *
[5] 0x0113106e CLASS::foo2(void) *
[6] 0x01131172 CLASS::foo3(void) *
我想使用方法重载,我至少可以使用一种 hack(知道 MSVC 将使用的顺序,使用该顺序创建新标头,仅供 DLL 使用),但我宁愿让它正常工作首先。还是完全有可能?
【问题讨论】:
如果我是你,我会放弃尝试在应用程序和 DLL 之间传递 C++ 类,除非它们都使用相同版本的相同编译器编译。我会创建一个 C API。如果无法更改 DLL,则可以将自己的 vtable 制作为 struct。 【参考方案1】:即使使用不同的编译器,您也可以在模块之间使用 C++ 接口,但您只是遇到了一个极端情况。这就是接口跨不同编译器工作的原因:可用的 C++ 编译器必须生成与 COM 接口一起工作的代码。 COM 对象看起来很像具有单个 vtable 的 C++ 对象,其指针位于对象的偏移量为零处。问题是,据我所知,COM 没有为重载方法定义任何规则,但只要您不想利用方法重载和其他一些奇异的语言特性,那么您就可以信任方法顺序(因为 COM兼容性)。
即使您不在模块之间使用 C++ 接口,您的模块之间的接口也应该相当简单。我从来不需要模块之间的接口中的方法重载。即使您需要重载,也可以像在根本没有重载的语言中一样,为方法提供单独的名称。在模块之间传递原始类型和简单的数据结构也是明智的,如果你只传递原始类型,那么很容易编译具有不同配置的模块(比如 Debug 中的一个和 Release 中的其他)以加快调试迭代。
【讨论】:
所以你的看法是,因为 COM 不使用重载,因此其他 Windows 编译器没有费心“复制”MS 的重载方法?这意味着我运气不好,要么不得不放弃超载,要么使用我提到的黑客? 至于“为方法提供单独的名称”,您的意思是我可以使用void foo1(); void foo1(int x);
而不是void foo1_void(); void foo1_int(int x);
?这将是解决此问题的一种方法,但使用这种方法,我将不得不根据参数类型调用单独的方法,而不是 cout、cin 和其他人使用 operator<<
的“流”方法。
我不会依赖编译器之间的重载之类的东西。但是,无论如何,如果你跨越模块边界,你必须知道许多其他(甚至更大)的事情,比如传递对象的内存管理......在我看来,使用 C++ 接口作为模块间接口仍然是一个非常好的事情。例如,当我使用 DLL 作为插件时,我通常只从模块中导出一个函数(使用平台相关的丑陋导出),然后这个导出的函数返回一个 C++ 接口。在 C 语言中,我通常返回一个指向包含函数 ptrs 的结构的指针...
在我看来,流方法很糟糕,但那是另一回事......我认为重载有时很方便(例如,当你有一个可以处理整数、浮点数的方法时......但是在接口方法的情况下,我不会尝试用重载来欺骗自己。期间。您可以查看其他 Windows 编译器是否复制了 Visual C++ 样式的顺序,我不知道...
我做的差不多。我在 DLL 中有一个函数可以在 DLL 中创建一个对象并返回一个转换为接口类的指针。到目前为止,这运行良好(由于 COM 兼容性)。所以我唯一传递的东西是原始类型,我在分配它的同一个模块中释放所有内存,等等。这是我第一次尝试使用重载方法,经过一些调试后意识到为什么,现在我需要知道的是有什么可以做的吗?如果您知道无法完成,您能否编辑您的答案以包含该内容,我会接受。以上是关于vfptr 中的 Visual C++ 方法以相反的顺序的主要内容,如果未能解决你的问题,请参考以下文章
Visual Studio 2008 中的 C# 项目中的 C++ 项目参考
如何编写代码以使用 microsoft visual c++ 调用 JNI [关闭]
如何在 Visual Studio 2017 中创建 Visual c++ MFC 控制台项目