在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,通过从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;
}