假设我'有一个接口类
class foo_if_t {
};
,第一个库libfoo_orig.so
,其类foo_t
继承自foo_if_t
并具有以下代码:
#include "foo_if.h"
class foo_t : public foo_if_t{
private:
int res;
public:
foo_t(){}
virtual ~foo_t(){}
};
,第二个库libfoo_mod.so
的类foo_t
重新定义如下:
#include "foo_if.h"
class foo_t : public foo_if_t{
private:
int res[100];
public:
foo_t() {
for (int i=0; i<100; i++)
res[i] = i;
}
virtual ~foo_t(){}
};
我创建一个符号链接libfoo.so --> libfoo_orig.so
并编译以下应用程序
#include "foo_orig.h"
int main(){
foo_if_t *foo_if = new foo_t();
delete foo_if;
}
with g++ -ggdb -O0 test.cpp -o test -L. -lfoo
(因此链接到libfoo.so)。
此时我将符号链接更改为目标libfoo_mod.so
并重新运行代码。这将导致以下错误:
*** Error in `./test': free(): invalid next size (fast): 0x0000000001ec9010 ***
Aborted (core dumped)
我认为可能发生的事情是,来自库foo_t
的{{1}}的构造函数分配占用的堆大于foo_orig.so
的堆,所以当{{1}时调用foo_mod.so
构造函数,它会使堆内存超出分配边界(因此会破坏堆foo_mod.so
块引用)。这告诉我堆预留在链接时以某种方式预先计算,而我认为它将在运行时根据调用的实际构造函数动态解析。我错了,如果没有,为什么生成的输出代码表现得像这样?
作为反证明测试,我在为每个库编写的foo_t
实现中包装next
调用;从主代码调用new foo_t()
可以正常工作。
答案 0 :(得分:2)
这里的问题是你的库代码和主程序代码对foo_t
的布局结构有不同的看法。尽管它们都识别出类型的存在,但它们都使用不同版本的头文件进行编译来定义它。这总会导致大麻烦。
在您的情况下,问题是由new
执行的实际内存分配是从主程序编译的,因此可以了解创建的对象的大小。同时,构造函数使用库代码进行编译,并且对底层对象大小有完全不同的概念。因此,主程序在堆上创建一个sizeof(foo_t)
的对象 - 从它的角度来看是sizeof(int)
。构造函数,不知道这一点,愉快地使内存高达100英寸,从而破坏了堆。
基本上你不能欺骗&#39;通过这种方式。如果你改变头文件,你应该总是重新编译依赖于这个头的库,或者面对这样的不可预测的麻烦(显然你在这种情况下故意这样做,但这也很容易在意外完成)。