C++ vtable 修改与对象析构事件处理

C++编程中,vtable(虚函数表)是一个重要的概念,它允许多态的实现。本文将探讨如何通过修改vtable来处理对象的析构事件,特别是在需要对大量对象进行管理时。

假设有一个包含超过100,000个对象(例如Book)的容器(例如Library),并且该容器会不断地被工作线程修改。这些对象在GUI控件中被可视化,并且有一个活跃的Book对象。当工作线程删除了活跃的Book对象,而GUI线程试图访问它时,问题就出现了。

解决方案评估

评估了几种解决方案,其中之一就是v-table重载。虽然互联网上有许多关于构建v-table的讨论,但这些讨论可能不够通用,因为编译器可以自由选择v-table的格式。因此,决定使用编译器本身来构建自定义v-table。

自定义v-table

实现目标的更复杂部分是构建v-table,但v-table的内存布局可能是编译器特定的。因此,考虑使用编译器智能来构建自定义v-table,通过从Book类继承一个新类(例如book_New),而不添加任何状态信息,如成员变量或访问基类成员变量,并在必要时重载虚拟函数。现在,book_New的v-table将被钩入book对象的实例。

使用代码

示例代码实现了一个模板,用于钩住对象销毁(这是主要目的)。示例代码目前只支持钩住一个对象和一个观察者,但模板可以修改以支持多个对象和钩住其他成员函数。

class Book { protected: char *name_; public: Book(char *pstr) { name_ = _strdup(pstr); }; Book(){}; void Display() { printf("\nBook Name:%s\n", name_);}; virtual ~Book() { printf("\nvirtual BOOK::~BOOK for :%s\n", name_); delete name_; }; };

这是一个简单的C++类,它记住了书名,并具有虚拟析构函数。保持虚拟析构函数的原因是需要钩住销毁事件。

class IDestructorObserver { public: // 钩住对象被销毁时调用 virtual void OnDestroy(T* pObj) = 0; };

这是一个简单的C++接口,它有一个事件回调用于销毁,并将有问题的对象作为参数传递。容器应该实现这个接口,以便得到对象的销毁通知。

T* data_; // 要存储的通用指针 IDestructorObserver* lifeTimeObserver_; // 用于分派事件 void *vtable_; // 用于存储Book的vtable指针以备将来引用

data_引用了感兴趣的对象,这里这个变量最终存储了Book对象,lifeTimeObserver_用于向感兴趣的方分派事件,最后vtable_用于存储Book的vtable指针以备将来引用。这些数据成员应该保存在向量或映射类型的数据结构中,以支持多个对象和多个观察者。

static DestructorHook& Instance(IDestructorObserver* proc) { staticObj.lifeTimeObserver_ = proc; staticObj.name_ = _strdup("Librarian Hook"); return staticObj; };

实例方法将初始化lifeTimeObserver_成员并返回Hook对象。返回的Hook对象将用于钩住对象。

void Hook(T* objPtr) { UnHook(); memcpy(&vtable_, objPtr, sizeof(void*)); memcpy(objPtr, this, sizeof(void*)); data_ = objPtr; }

Hook方法将备份钩住对象的vtable,并将其保存以供将来参考,然后使用自定义vtable覆盖它。

virtual ~DestructorHook() { staticObj.unHook(); if (this == staticObj.data_) { staticObj.lifeTimeObserver_->OnDestroy(staticObj.data_); staticObj.data_ = 0; } }

析构函数将在钩住的对象被删除时调用,事件将在过滤自毁后分派给订阅者。

主函数

int _tmain(int argc, _TCHAR* argv[]) { Book *pBook[5]; char name[20] = {0}; for (int i = 0; i < 5; i++) { sprintf_s(name, "book%d", (i+1)); pBook[i] = new Book(name); } Librarian librarian; BookDestructorHook& hook_ = BookDestructorHook::Instance(static_cast(&librarian)); hook_.Hook(pBook[3]); printf("\nDisplay available books\n"); for (int i = 0; i < 5; i++) { pBook[i]->Display(); } printf("\n\nDeleting all Book Librarian should get notification for\t"); pBook[3]->Display(); for (int i = 0; i < 5; i++) { delete pBook[i]; } return 0; }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485