如果我new
在我的库中使用关键字(它的构建方式与我的主应用程序不同),当我在我的主应用程序中使用 删除它时delete
,是否有可能出现崩溃/错误?
6 回答
这取决于。如果您谈论的是静态库,那么您可能会没事——代码将在与主程序相同的上下文中运行,使用相同的 C++ 运行时库。这意味着new
并且delete
将使用相同的堆。
如果您谈论的是共享库(DLL),那么您可能不会好。在 DLL 中运行的代码可能使用不同的 C++ 运行时库,这意味着堆的布局会有所不同。DLL 可能完全使用不同的堆。
调用delete
(在主程序中)由 DLL 分配的指针(反之亦然)将导致(最好的情况)立即崩溃或(最坏的情况)需要一段时间才能追踪的内存损坏。
你有几个选择。第一种是使用“工厂方法”模式来创建和删除这些对象:
Foo *CreateFoo();
void DeleteFoo(Foo *p);
这些不应该在头文件中实现。
或者,您可以Destroy
在对象上定义一个方法:
class Foo
{
~Foo();
public:
virtual void Destroy();
};
...再次,不要在头文件中实现它。你会这样实现它:
void Foo::Destroy()
{
delete this;
// don't do anything that accesses this object past this point.
}
请注意, Foo 的析构函数是私有的,因此您必须调用Foo::Destroy
.
Microsoft COM 做了类似的事情,它定义了一个Release
方法,当对象的引用计数降至零时删除该对象。
是的。特别是,您会看到调试/发布堆不同的问题,如果您的库使用新放置或任何自定义堆,您也会遇到问题。调试/发布问题是迄今为止最常见的问题。
是的你将会。一个简单的解决方案是在您的库中提供可从主应用程序调用的 Create 和 Delete 函数。Create 函数将执行 new 并返回一个指针,该指针稍后将传递给 Delete 函数进行删除。
这是我只在 Windows 上看到的问题。
Unixish 系统不习惯强制共享库链接到同一程序中同一库的不同版本,并且所有加载的符号都是全局可见的。这意味着如果一个对象在代码的一部分中分配并在另一部分中删除,则两者都使用相同的系统库来执行此操作。
我不得不说,Windows 使用其各种 C 运行时 DLL 创建的这个问题对于 C 程序员来说确实很烦人且不自然。查看 C 库;它具有类似 strdup 的函数,可以对字符串进行 malloc 并期望程序员对其调用 free() 。但是在你自己的 Windows 库中做同样的事情,然后等待爆炸。您也必须等待,因为它不会在开发过程中发生,但只有在您将已编译的 DLL 提供给其他一些可怜的 sap 之后才会发生。
Old New Thing之前已经介绍过这一点。他还列出了微软的主要解决方案。
你说得对,那里有问题,但在大多数情况下,有一个比其他答案(到目前为止)提出的更简单的解决方案。您可以继续自由地使用 new 和 delete —— 您需要做的就是为库中可能跨 DLL 边界使用的每个类重载 new 和 delete。
就个人而言,我只是定义了一个简单的类来提供所需的功能:
class NewDelete
{
public:
void *operator new (size_t size);
void operator delete (void *memory);
void *operator new (size_t size, void *ptr);
void operator delete (void *memory, void *ptr);
};
只要这四个成员函数都定义在同一个 DLL 中,那么从这个类派生的任何类都自动是“DLL 安全的”——new 和 delete 可以在它们上正常使用,而不必担心 DLL 边界。