C++/CLI代码中的内存泄漏.我做错了什么
memory leaks in C++/CLI code.. what did I do wrong?
用C++编写无内存泄漏的代码对我来说不是问题,我只是坚持RAII习惯用法。
在C#中编写无内存泄漏的代码也不是很难,垃圾收集器会处理它
不幸的是,编写C++/CLI代码对我来说是个问题。我以为我已经理解了它的工作原理,但我仍然有很大的问题,希望你能给我一些提示。
这就是我所拥有的:
用C#编写的Windows服务,在内部使用C++库(例如OpenCV)。使用C++/CLI包装类访问C++类。例如,我有一个cv::Mat
映像对象的MatW
C++/CLI包装类,它将System::Drawing::Bitmap
:作为构造函数参数
public ref class MatW
{
public:
MatW(System::Drawing::Bitmap ^bmpimg)
{
cv::Size imgsize(bmpimg->Width, bmpimg->Height);
nativeMat = new Mat(imgsize, CV_8UC3);
// code to copy data from Bitmap to Mat
// ...
}
~MatW()
{
delete nativeMat;
}
cv::Mat* ptr() { return nativeMat; }
private:
cv::Mat *nativeMat;
};
另一个C++类可能是例如
class PeopleDetector
{
public:
void detect(const cv::Mat &img, std::vector<std::string> &people);
}
及其包装类:
public ref class PeopleDetectorW
{
public:
PeopleDetectorW() { nativePeopleDetector = new PeopleDetector(); }
~PeopleDetectorW() { delete nativePeopleDetector; }
System::Collections::Generic::List<System::String^>^ detect(MatW^ img)
{
std::vector<std::string> people;
nativePeopleDetector->detect(*img->ptr(), people);
System::Collections::Generic::List<System::String^>^ peopleList = gcnew System::Collections::Generic::List<System::String^>();
for (std::vector<std::string>::iterator it = people.begin(); it != people.end(); ++it)
{
System::String^ p = gcnew System::String(it->c_str());
peopleList->Add(p);
}
return peopleList;
}
这是对我的Windows服务C#类中的方法的调用:
Bitmap bmpimg = ...
using (MatW img = new MatW(bmpimg))
{
using (PeopleDetectorW peopleDetector = new PeopleDetector())
{
List<string> people = peopleDetector.detect(img);
}
}
现在,我的问题是:
- 我的代码有什么问题吗
- 我的C#代码中必须使用
using
吗?当有多个包装器对象在使用时,这会使代码变得丑陋,因为using
语句必须嵌套 - 在使用了这些对象之后,我可以使用
Dispose()
吗 - 我能不麻烦把它交给垃圾收集器吗?(无
using
,无Dispose()
) - 上面的代码是将
List<string^>^
之类的对象从C++/CLI返回到C#的正确方法吗 - 使用
gcnew
是否意味着垃圾收集器将处理对象,而我不必关心如何以及何时释放它们
我知道有很多问题,但我只想消除我的内存泄漏,所以我列出了我认为可能出错的一切。。。
我的代码有什么问题吗?
不在您发布的内容中-您正确应用了using
语句。因此,您的代码示例并不是内存泄漏的原因。
我的C#代码中必须使用吗?当有多个包装器对象在使用时,这会使代码变得丑陋,因为using语句必须嵌套
你不必这样做,但你不必在语法上嵌套它们。这相当于:
Bitmap bmpimg = ...
using (MatW img = new MatW(bmpimg))
using (PeopleDetectorW peopleDetector = new PeopleDetector())
{
List<string> people = peopleDetector.detect(img);
}
在使用对象后,我可以使用Dispose()吗?
您可以,但您需要一个try
/finally
来确保始终调用Dispose
,即使在抛出异常时也是如此。using
语句封装了整个模式。
我能不麻烦,把它留给垃圾收集器吗?(不使用,不处置())
C++RAII通常应用于各种临时状态清理,包括减少构造函数中递增的计数器等。而GC在后台线程中运行。它并不适用于RAII可以处理的所有确定性清理场景。RAII的CLR等价物是IDisposable
,它的C#语言接口是using
。在C++中,它的语言接口(自然)是析构函数(成为Dispose
方法)和delete
运算符(成为对Dispose
的调用)。声明为"在堆栈上"的Ref类对象实际上相当于C#
使用模式。
上面的代码是将List^之类的对象从C++/CLI返回到C#的正确方法吗?
差不多!
使用gcnew是否意味着垃圾收集器将处理对象,而我不必关心如何以及何时释放它们?
您不必释放内存。但是,如果类实现IDisposable
,那么这与内存分配是完全不同的问题。在放弃对象之前,必须手动调用Dispose
。
要警惕终结器——这是一种挂接到GC的方式,以便在GC收集对象时运行您自己的清理代码。但它们并不是真正适合在应用程序代码中通用的。它们从一个你不控制的线程运行,在一个你不能控制的时间运行,并且以一个你无法控制的顺序运行。因此,如果一个对象的终结器试图用终结器访问另一个对象,那么第二个对象可能已经被终结了。无法控制这些事件的顺序。现在,SafeHandle涵盖了终结器的大多数原始用途。
ref类中没有终结器。
在C++/CLI中,当在堆栈上创建类的实例(C++样式),然后它超出作用域时,或者使用delete
运算符时,都会调用析构函数。终结器由GC在完成对象时调用。
在C#中,GC处理所有对象的销毁(没有删除运算符),因此析构函数和终结器之间没有区别。
因此,带有~的">析构函数"的行为类似于c++析构函数,而一点也不像c#析构函数。C++/CLI ref类中的">析构函数"被编译到.NetDispose()
方法中
与C#析构函数/终结器等价的是用定义的终结器方法(感叹号)。
因此,为了避免内存泄漏,您需要定义一个终结器:
!MatW()
{
delete nativeMat;
}
~MatW()
{
this->!MatW();
}
请参阅MSDN文章visual C++中的析构函数和终结器使用
- #定义c-预处理器常量..我做错了什么
- 努力将整数转换为链表。不知道我在这里做错了什么
- 首要问题的答案让值班员搞错了
- 看起来is_nothrow_constructible_v()在MSVC中被破坏了,我错了吗
- .h 和.cpp文件分离时出错,但仅使用 .h 文件时没有错误.我做错了什么?
- 我的C++线程做错了什么?
- 如何正确使用 >=?(a+f()+c)>=0 错了吗?
- 谁能告诉我我用 getline 做错了什么 (cpp) 格式
- 没有输出的合并排序我做错了什么?
- 我正在尝试使用 while 循环从字符串中删除字母,直到没有字母。我在这里做错了什么?
- 在C++中使用 AKS 素数测试计算双胞胎素数 我做错了什么?
- 指针相关的UE4崩溃.我的指针哪里错了?
- 我一直试图弄清楚我在这个链表程序中做错了什么
- 我正在尝试学习如何在 c++ 中传递指针,但出现错误:没有用于调用"test"的匹配函数。我做错了什么?
- FFMPEG,C++,内存泄漏,我做错了什么?
- 我做错了什么?反向字符串 C++
- 我在这个课上做错了什么?
- 创建整数的 2D 数组,该数组将使用两个函数用随机数填充矩阵.我做错了什么?
- 在不使用内置库函数的情况下添加字符串,我做错了什么?
- C++/CLI代码中的内存泄漏.我做错了什么