要添加到其他答案,请考虑在运行时可以覆盖通过共享对象或DLL(取决于平台)公开的任何函数.Linux提供了LD_PRELOAD环境变量,它可以指定要在所有其他对象之后加载的共享对象,这可以用于覆盖任意函数定义.它实际上是为单元测试目的提供"模拟对象"的最佳方式,因为它不是真正的入侵.但是,与其他形式的猴子修补不同,请注意这样的变化是全球性的.您不能在不影响其他呼叫的情况下将一个特定呼叫指定为不同.
不是那么便宜,而且由于大项目的危险,你最好有充分的理由.
预处理器可能是最好的候选者,因为它对语言本身一无所知.它可用于重命名属性,方法和其他符号名称 - 但替换是全局的,至少对于单个#include或代码序列.
之前我曾经用它来击败"库钻石" - 图书馆A和B都导入了OS库S,但是以不同的方式使得S的某些符号具有相同的名称但不同.(命名空间是不可能的,因为它们会产生更深远的影响).
同样,您可以使用兼容但更高级的类替换符号名称.例如,在VC中,#import生成一个使用_bstr_t类型适配器的导入库.在一个项目中,我已经成功地取代了这些_bstr_t与其他代码更好的互操作兼容,足以类使用,仅仅是#define"荷兰国际集团_bstr_t作为我的替换类#import.
修补虚拟方法表 - 替换整个VMT或单个方法 - 是我遇到的其他一些问题.它需要很好地理解编译器如何实现VMT.我不会在现实生活项目中这样做,因为它依赖于编译器内部,并且当符号发生变化时你不会得到任何警告.但是,了解C++的实现细节是一个有趣的练习.一个应用程序将在运行时从初始化程序/加载程序存根切换到完全或甚至数据相关的实现.
在某些情况下,动态生成代码很常见,例如转发/过滤COM接口调用或将OS窗口句柄映射到库对象.我不确定这是否仍然是"猴子修补",因为它并没有真正用语言本身.
考虑到猴子修补的“游击第三方库使用”方面,C++ 提供了许多设施:
const_cast让您可以围绕热心const声明进行工作。#define private public在包含标头之前,您可以访问私有成员。use Parent::protected_field允许您访问受保护的成员。但是,如果您正在使用的第三方内容是已编译的,那么在动态语言中可行的大多数事情就不那么容易了,而且通常根本不可能。
| 归档时间: |
|
| 查看次数: |
3326 次 |
| 最近记录: |