C++ 类库动态运行时分配

标签 c++ runtime size alloc

假设我有一个接口(interface)类

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(){}
};

我创建一个符号链接(symbolic link) libfoo.so --> libfoo_orig.so 并编译以下应用程序

#include "foo_orig.h"

int main(){
    foo_if_t *foo_if = new foo_t();
    delete foo_if;
}

g++ -ggdb -O0 test.cpp -o test -L。 -lfoo(因此链接到 libfoo.so)。

此时我将符号链接(symbolic link)更改为目标libfoo_mod.so并重新运行代码。这将导致以下错误:

*** Error in `./test': free(): invalid next size (fast): 0x0000000001ec9010 ***
Aborted (core dumped)

我认为可能发生的情况是,库 foo_orig.so 中的 foo_t 的构造函数分配占用的堆 block 比来自 foo_mod 的堆 block 要小.so,因此当调用 foo_mod.so foo_t 构造函数时,它会弄脏超出分配边界的堆内存(从而损坏堆 next block 引用)。这告诉我,堆预留是在链接时以某种方式预先计算的,而我认为它将在运行时根据调用的实际构造函数代码动态解析。我是否弄错了,如果没有,为什么生成的输出代码会这样?

作为反证明测试,我将 new foo_t() 调用包装在为每个库编写的 static foo_it_t * foo_if_t::new_instance() 实现中;从主代码调用 new_instance 可以正常工作。

最佳答案

这里的问题是你的库代码和主程序代码对foo_t的布局结构有不同的想法。尽管它们都认识到该类型的存在,但它们都是使用定义该类型的不同版本的 header 进行编译的。这总是会带来大麻烦。

在您的情况下,问题是由以下事实引起的:由 new 执行的实际内存分配是从主程序编译的,因此知道创建的对象的大小是多少是。同时,构造函数是用库代码编译的,并且对底层对象大小有完全不同的想法。因此,主程序在堆上创建了一个 sizeof(foo_t) 对象 - 从它的角度来看,它是 sizeof(int) 。构造函数不知道这一点,很乐意将内存删除最多 100 个整数,从而损坏了堆。

基本上你不能用这种方式“作弊”。如果您更改头文件,您应该始终重新编译依赖于该头文件的库,否则会面临像这样不可预测的麻烦(显然在这种情况下您是故意这样做的,但这也很容易意外完成)。

关于C++ 类库动态运行时分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37114376/

相关文章:

android - C++ 与 iOS/android 的 native 实现

c++ - 如何推断 CRTP 中的类型?

c++ - 这是 gcc 的重载解析中的错误吗?

java - 在 Java 中抑制运行时控制台警告的最佳方法是什么?

arrays - 如何知道 Fortran 数组中非零元素的个数?

C++、线程和指针

algorithm - 为什么对图的边进行排序需要 O(E log E) 时间?

c# - 获取只有字符串的嵌套枚举类型?

c - 指向结构时如何正确使用 malloc() 和 realloc()?

mysql - 是否可以限制 mySQL 中文本类型字段的大小?