假设我的钻石继承情况如下:
class A{
public: virtual void foo(){};
};
class B: public virtual A{
public: virtual void foo(){};
};
class C: public virtual A{
public: virtual void foo(){};
};
class D: B, C{};
Run Code Online (Sandbox Code Playgroud)
引用歧义的最后一行产生编译错误.据我所知,问题是编译器不知道在D的vtbl中放置哪个foo,但是如果它没有定义自己的任何虚函数,为什么D甚至会有一个vtbl呢?
偶尔当使用GCC时,我会得到如下的神秘错误:
未定义引用'vtable for classname'
当它不是由缺少的库引起时,这个不那么描述性的错误消息总是让我逐行挖掘代码文件以找到虚函数的缺失实现.有没有办法让链接器告诉我它缺少哪个虚函数,可能是一个标志或什么?或者它可能告诉我但我不明白它在说什么?
问候StackOverflowians,
正如此处所发现的,Windows 7具有一个错误,在该错误中,Windows资源管理器实例不会触发DISPID_BEFORENAVIGATE2事件.此事件允许在即将进行导航时通知shell扩展,并且(对我来说最重要的)有机会取消导航.我已经找了很长时间的解决方法,我想我找到了一个.但是,我想对它的安全性有一些看法.
我最近一直在玩API挂钩,我已经用它来为我的扩展程序挂钩一些函数了.我注意到IShellBrowser中有一个控制导航的功能.起初我以为你不能挂钩这样的东西,但在阅读COM对象的布局后,我意识到应该可以通过从任何活动实例的vtable中抓取正确的函数指针来实现.果然,它像梦一样.设置挂钩后,所有资源管理器窗口中的所有导航都会在我的绕行功能中运行,我可以根据目标pidl决定是否拒绝它们.
所以我的问题是,我有什么理由不这样做吗?我从来没有听说过用于挂钩COM对象函数的API钩子.是否存在不起作用的情况?危险吗?(至少比常规的API挂钩更多)
相关代码如下.我正在使用MinHook,这是一个简约的挂钩库,它使用了经过验证的蹦床功能方法.
typedef HRESULT (WINAPI *BROWSEOBJECT)(IShellBrowser*, PCUIDLIST_RELATIVE, UINT);
HRESULT WINAPI DetourBrowseObject(IShellBrowser* _this, PCUIDLIST_RELATIVE pidl, UINT wFlags);
BROWSEOBJECT fpBrowseObject = NULL;
BROWSEOBJECT ShellBrowser_BrowseObject = NULL;
bool Initialize() {
if(MH_Initialize() != MH_OK) {
return false;
}
// Get a reference to an existing IShellBrowser. Any instance will do.
// ShellBrowser enum code taken from The Old New Thing
IShellWindows *psw;
BOOL fFound = FALSE;
if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, …Run Code Online (Sandbox Code Playgroud) 正是这个话题所要求的.还想知道为什么非CRTP的常见例子没有提到virtualdtor.
编辑:伙计们,请发布关于CRTP问题,谢谢.
我正在尝试编写一个基本上像indianwebproxy一样工作的Http代理
所以我启动了qtcreator,但是我的一个类无法使用臭名昭着的错误进行编译:undefined reference to vtable for HttpProxyThreadBrowser.我无法弄清楚为什么这样做.我在Stackoverflow上阅读了类似的问题,显然问题是未定义的虚拟方法不纯,但我还没有声明任何虚函数.这是我的课
class HttpProxyThreadBrowser : public QThread
{
public:
HttpProxyThreadBrowser(QTcpSocket outgoingSocket,QTcpSocket browserSocket,QObject *parent = 0);
~HttpProxyThreadBrowser(){};
void run();
private:
QTcpSocket outgoingSocket;
QTcpSocket browserSocket;
};
Run Code Online (Sandbox Code Playgroud)
我在这里用pastebin定义了这个类,以免给你带来烦恼.不幸的是,我无法找出为什么vtable未定义.请协助.
httpproxythreadbrowser.cpp:5: undefined reference to `vtable for HttpProxyThreadBrowser
collect2: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud) 是否可以从 C# 调用函数到结构中的非托管函数(通过 VTable)。
例如,我正在挂接一个应用程序,我正在为每个类(应用程序的)重新创建结构。
public struct SomeStruct {
[FieldOffset(0x00)]
public IntPtr * VTable;
[FieldOffset(0x10)]
public uint SomeValue;
}
Run Code Online (Sandbox Code Playgroud)
然后,我通常这样做:
var * data = (SomeStruct*)(Address);
Run Code Online (Sandbox Code Playgroud)
我希望通过以下任一方式从结构的 VTable 调用函数
Invoke<delegate>(data->VTable[0x3C])(delegateArguments)
Run Code Online (Sandbox Code Playgroud)
或者
var eax = Invoke<Func<uint,uint>(data->VTable[0x3C])(arg1,arg2)
Run Code Online (Sandbox Code Playgroud)
此外,这是否可以有效地完成(因为这些 vtable 函数可以被多次调用)?
也许通过反射发射?
据我所知,每次调用Invoke<>func时,编组都必须创建委托函数。
这个问题被多次以类似的方式提出,例如在stackoverflow或forum.qt.io或qtcentre.org。问题是这个错误消息非常模糊,以至于一种解决方案无法应用于另一种场景。不过,大多数线程都在讨论中死掉了:-(
所以我在 Qt 应用程序中收到的完整错误消息是:
找不到“OneOfMyClasses”
值的虚拟表的链接器符号,而是找到“QString::shared_null”
OneOfMyClasses根据各种情况而变化,QString::shared_null对于我收到的所有错误保持不变。这是我的日志控制台的屏幕截图:
它发生的点是在当前位置之前的源代码行中的该函数中(黄色箭头):
因此,根据我进入的消息m_pStateWidget->insertNavLabel(...),错误消息打印在与 QString 类相关的 Qt 内的构造函数中的某个位置。所以我尝试了以下方法,将问题从该代码位置移开:
执行此操作时,我在下面的几行代码中收到相同的错误消息,消息中包含另一个类名,请注意 QString ::shared_null保持不变。
在我看来,我的记忆有某种损坏。
感谢您的任何提示或帮助!:-)
编辑:现在变得非常有趣。在打印消息之前我已经进入了每个函数,最终得到了这些错误消息:
在这个位置:
当我在 QtCreator 中浏览调用堆栈时,每次我在堆栈中选择另一个函数时,都会一次又一次地打印错误。
当我在 C++ 中定义类时,我总是将 dtor 定义为 virtual。这是我保护自己的方法,以防我编写继承类。
我想知道即使我不会继承该类,我是否也要支付性能开销。
例如:
class A final
{
A();
virtual ~A(){printf("dtor");}
};
Run Code Online (Sandbox Code Playgroud)
当我使用此类时,dtor 实际上会通过 vtable 调用还是会作为静态 dtor 实现?
令 vptr 为指向 vtable 的指针,该 vtable 随其类受虚拟函数约束的对象一起携带。
“vptr”的改变可能不是预期的行为。然而,这种更改不能被检测为非法内存访问,因为指针位于已分配内存的边界内。据我所知,“Efence”和“valgrind/memcheck”不会有帮助。
然而,意外的 vptr 更改可能会导致严重的问题。如果 vptr 被更改为指向任意内存,则删除运算符可能会立即导致分段错误。
是否有任何东西可以在 vtable 或指向 vtable 的指针上设置“守卫”,以便监视任何更改?
Clang++ 似乎并没有完成全部工作。给定
#include <string.h>
#include <stdio.h>
struct X { virtual ~X() {} };
int main(int argc, char** argv)
{
X x;
memset((void*)&x, 0, sizeof(X));
printf("<before exit>\n");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译并执行...
> clang++ -fsanitize=undefined -fsanitize=vptr tmp.cpp -o test
> ./test
Run Code Online (Sandbox Code Playgroud)
在调用“virtual ~X()”时检测违规行为。
<before exit>
<unknown>: runtime error: member call on address 0xbfe30ab8 which does not point to an object of type 'X' …Run Code Online (Sandbox Code Playgroud) C1,C2,...是回调类.
它们来自CBase与回调的通用接口CBase::f().
所有这些都CBase::f()用final修饰符覆盖.
我必须注册派生 的任何类的C1 ~50个实例,以及派生自的任何类的 ~50个实例C2.
(@@例如,见下面的代码)
主要目标:当我打电话时allF(), C1::f()/ C2::f()每个注册的实例都必须被调用.
这是一个简化版本,它的工作原理(完整演示): -
#include <iostream>
#include <vector>
class CBase{
public: virtual void f(){std::cout<<"CBase"<<std::endl;}
};
class C1 : public CBase{
public: virtual void f() final{std::cout<<"C1"<<std::endl;}
};
class C2 : public CBase{
public: virtual void f() final{std::cout<<"C2"<<std::endl;}
};
Run Code Online (Sandbox Code Playgroud)
这是回调注册: -
//-------- begin …Run Code Online (Sandbox Code Playgroud)