为什么'allocate in one library and free in the other'是错误的
Why it is a mistake to 'allocate in one library and free in the other'
Google在Android ndk指南网站上写道:
在一个库中分配的内存,并在另一个库中释放内存,从而导致内存泄漏或堆损坏。
- 为什么?
- 它总是正确的?
编辑
正如@Galik所写,这句话的上下文是:
在C++中,在单个程序中定义同一函数或对象的多个副本是不安全的。这是C++标准中存在的一个定义规则的一个方面。
使用静态运行时(以及一般的静态库(时,很容易意外违反此规则。例如,以下应用程序违反了此规则:
。
在这种情况下,STL(包括全局数据和静态构造函数(将存在于两个库中。此应用程序的运行时行为未定义,实际上崩溃非常常见。其他可能的问题包括:
- 在一个库中分配的内存,并在另一个库中释放内存,从而导致内存泄漏或堆损坏。
- libfoo.so 引发的异常未在 libbar.so 中捕获,从而导致应用崩溃。
- 标准::cout 的缓冲无法正常工作。
它被认为是错误的一个可能原因是,通常分配带有一定的初始化,而释放带有一些破坏逻辑。
理论:
主要危险是初始化/破坏逻辑不匹配。
让我们将两个不同的 STL 版本看作两个不同且独立的库。
考虑一下:每个库都允许您分配/取消分配某些内容。在获取资源时,每个图书馆都以自己的方式对那个东西进行一些内务管理,这些方式被封装起来(阅读:你不知道它,也不需要(。 如果每个人所做的家政工作明显不同,会发生什么?
例:
class Foo
{
private:
int x;
public:
Foo() : x(42) {}
};
namespace ModuleA
{
Foo* createAFoo()
{
return new Foo();
}
void deleteAFoo(Foo* foo)
{
if(foo != nullptr)
delete foo;
}
}
namespace ModuleB
{
std::vector<Foo*> all_foos;
Foo* createAFoo()
{
Foo* foo = new Foo();
all_foos.push_back(foo);
return foo;
}
void deleteAFoo(Foo* foo)
{
if(foo != nullptr)
{
std::vector<int>::iterator position = std::find(all_foos.begin(), all_foos.end(), foo);
if (position != myVector.end())
{
myVector.erase(position);
}
delete foo;
}
}
}
问题:如果我们执行以下操作会发生什么?
Foo* foo = ModuleB::createAFoo();
ModuleA::deleteAFoo(foo);
答:ModuleB
现在有一个悬空的指针。 这可能会导致各种可怕且难以调试的问题。 我们也没有all_foos
更小,这可能会被视为内存泄漏(每次指针的大小(。
问题:如果我们执行以下操作会发生什么?
Foo* foo = ModuleA::createAFoo();
ModuleB::deleteAFoo(foo);
答:看起来像...没有发生任何不好的事情! 但是,如果我删除了if (position != myVector.end())
支票怎么办?然后我们就会遇到问题。 STL 可能会以优化的名义做到这一点,所以......
我写了文档的那部分。我不得不调试一个问题,其中一个标准流对象(cout
或类似对象(被双重链接到库,导致该对象的两个不同实例。对象的构造函数运行了两次,但在对象的同一实例上运行了两次。一个对象被双重初始化,另一个对象未初始化。当使用未构造的对象时,它将尝试访问一些未初始化的内存并崩溃。
未定义行为的奇怪性真的没有限制。我记得的错误完全有可能是我们当时使用的编译器、链接器或加载器版本所独有的。
编辑:这是一个重现案例:
// foo.cpp
#include <stdio.h>
class Foo {
public:
Foo() { printf("this: %pn", this); }
};
Foo foo;
// main.cpp
int main() {
}
构建方式:
$ clang++ --version
clang version 7.0.0 (trunk 330210)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/bin
$ clang++ foo.cpp -shared -o libfoo.so
$ clang++ foo.cpp -shared -o libbar.so
$ clang++ main.cpp -L. -lfoo -lbar -rpath '$ORIGIN'
libfoo 和 libbar 都将被加载,并且每个都有自己的对象副本。构造函数将运行两次,但如您所见,只有对象的一个实例运行了构造函数;它只是运行两次。
$ ./a.out
this: 0x7f9475d48031
this: 0x7f9475d48031
- netcat command in c++
- Difference in displaying cv2 Mat
- C++ MFC Libraries in Travis CI
- 如何在OpenSSL中从configuration.h.in获取configuration.h
- 创建具有 new in 函数和"this is nullptr"异常的对象
- IN, OUT, INOUT Parameters
- 应用程序崩溃并显示"symbol _ZdlPvm, version Qt_5 not defined in file libQt5Core.so.5 with link time reference"
- 有人安装"IITB Simplecpp in mac"吗?
- 从 C 样式字符串中删除子字符串 "in place" 在C++代码中
- 如何修复"error: ‘_1’ was not declared in this scope"?
- Softmax Implementation in C++
- "std::list::splice(std::const_iterator pos, std::list&& other)"是否保证将"其他"留空?
- 将 out/in out 参数与 if/switch 的 init 语句一起使用
- IF-nesting in c++
- Gurobi GRBModel and GRBmodel in C++
- Tensorflow Hub in C++
- Centos7 g++ "to_string is not in a member of std"
- InitializeCriticalSectionEx Not Located In KERNEL32.Dll
- 将 lambda 表达式传递给 std::function in C++
- 为什么'allocate in one library and free in the other'是错误的