所以,我有一个C ++库,其中包含MSVCRT的静态链接副本。我希望任何人都可以将我的库与任何版本的MSVC Runtime一起使用。实现这一目标的最佳方法是什么?
我已经非常小心如何完成任务。
然而,我仍然有一些简单的代码导致堆损坏。
我的库中有一个这样的对象:
class Foos
{
public: //There is an Add method, but it's not used, so not relevant here
DLL_API Foos();
DLL_API ~Foos();
private:
std::map<std::wstring, Foo*> map;
};
Foos::~Foos()
{
// start at the begining and go to the end deleting the data object
for(std::map<std::wstring, Foo*>::iterator it = map.begin(); it != map.end(); it++)
{
delete it->second;
}
map.clear();
}
然后我在我的应用程序中使用它,如:
void bar() {
Foos list;
}
从任何地方调用此函数后,我收到有关堆栈损坏的调试警告。如果我真的让它耗尽,它实际上会破坏堆栈和段错误。
我的调用应用程序是使用Visual Studio 2012平台工具编译的。该库是使用Visual Studio 2010平台工具编译的。
这是我绝对不应该做的事情,还是我实际上违反了使用多个运行时的规则?
答案 0 :(得分:9)
内存永远不会通过DLL屏障
但是,确实如此。实际上很多次。您的应用程序为类对象创建了存储,在本例中为堆栈。然后传递一个指向库中方法的指针。从构造函数调用开始。该指针在库代码中是 this 。
在这样的场景中出现的问题是它没有创建正确的存储量。你有VS2012编译器来查看你的类声明。它使用st2012 :: map的VS2012实现。你的库是用VS2010编译的,它使用了一个完全不同的std :: map实现。尺寸完全不同。 C ++ 11带来了巨大的变化。
这只是工作中的完全内存损坏,应用程序中写入堆栈变量的代码将破坏std :: map。反过来说。
跨模块边界公开C ++类充满了类似的陷阱。只有在你可以保证所有内容都使用完全相同的编译器版本和完全相同的设置进行编译时才考虑它。没有捷径,你也不能混合使用Debug和Release构建代码。为了实现 no 实现细节而制作库肯定是可能的,你必须遵守这些规则:
那时你可以顺利编写COM代码,也就是你在DirectX中看到的样式。
答案 1 :(得分:3)
map
成员变量仍由应用程序创建,其中一些内部数据由应用程序而不是DLL分配(并且它们可能使用map
的不同实现)。根据经验,不要使用DLL中的堆栈对象,在DLL中添加类似Foos * CreateFoos()
的内容。
答案 2 :(得分:3)
运行时C ++对象不会跨越障碍(即矢量,地图, 等等......除非它们是在障碍的那一边创造的)
你正是这样做的。您的Foos对象由堆栈中的主程序创建,然后在库中使用。该对象包含一个地图作为其中的一部分...
编译主程序时,它会查看头文件等,以确定为Foos对象分配多少堆栈空间。并调用库中定义的构造函数...可能期望对象的布局/大小完全不同
答案 3 :(得分:0)
它可能不符合您的需求,但不要忘记在头文件中实现整个过程可以简化问题(某种程度): - )