当托管句柄保存在托管容器(IList)中时,是否有必要在 C++/CLI 中使用 GC::KeepAlive?
我对何时需要在 C++/CLI 包装器代码中使用 KeepAlive 以及其中如何处理生命周期感到困惑。考虑下面的代码并记下我询问是否需要 KeepAlive 的地方。
// convert from managed to native string
inline std::string ToStdString(String^ source)
{
if (String::IsNullOrEmpty(source))
return std::string();
int len = ((source->Length+1) * 2);
/*** Do I need GC::KeepAlive(source) here? ***/
char *ch = new char[ len ];
bool result ;
{
pin_ptr<const wchar_t> wch = PtrToStringChars( source );
result = wcstombs( ch, wch, len ) != -1;
}
std::string target = ch;
delete ch;
if(!result)
throw gcnew Exception("error converting System::String to std::string");
return target;
}
// convert from native to managed string
inline String^ ToSystemString(const std::string& source)
{
return gcnew String(source.c_str());
}
// unmanaged C++ class
struct NativeDog
{
std::string name;
std::string bark() const {return "woof";}
void eat(std::string& food) const {food.clear();}
};
typedef shared_ptr<NativeDog> NativeDogPtr;
// C++/CLI wrapper class
ref class ManagedDog
{
NativeDogPtr* base_;
NativeDog& base() {return **base_;}
ManagedDog() {base_ = new NativeDogPtr(new NativeDog);}
~ManagedDog() {if (base_) delete base_;}
!ManagedDog() {delete this;}
property String^ name
{
String^ get() {return ToSystemString(base().name);}
void set(String^ name)
{
base().name = ToStdString(name);
/*** Do I need GC::KeepAlive(name) here? ***/
}
}
String^ bark() {return ToSystemString(base().bark());}
void eat(String^ food)
{
std::string nativeFood = ToStdString(food);
base().eat(nativeFood);
food = ToSystemString(nativeFood);
/*** Do I need GC::KeepAlive(food) here? ***/
}
};
// unmanaged C++ class
struct NativeKennel
{
vector<NativeDogPtr> dogs;
};
// C++/CLI wrapper class
ref class ManagedKennel
{
NativeKennel* base_;
NativeKennel& base() {return *base_;}
IList<ManagedDog^>^ dogs;
void addDog(ManagedDog^ dog)
{
base().dogs.push_back(*dog->base_);
dogs->Add(dog);
/*** Do I need GC::KeepAlive(dog) here? Will the IList manage the ManagedDog lifetimes? ***/
}
};
I'm confused about when I need to use KeepAlive in my C++/CLI wrapper code and how lifetimes are handled in it. Consider the following code and note the places where I ask whether KeepAlive is needed.
// convert from managed to native string
inline std::string ToStdString(String^ source)
{
if (String::IsNullOrEmpty(source))
return std::string();
int len = ((source->Length+1) * 2);
/*** Do I need GC::KeepAlive(source) here? ***/
char *ch = new char[ len ];
bool result ;
{
pin_ptr<const wchar_t> wch = PtrToStringChars( source );
result = wcstombs( ch, wch, len ) != -1;
}
std::string target = ch;
delete ch;
if(!result)
throw gcnew Exception("error converting System::String to std::string");
return target;
}
// convert from native to managed string
inline String^ ToSystemString(const std::string& source)
{
return gcnew String(source.c_str());
}
// unmanaged C++ class
struct NativeDog
{
std::string name;
std::string bark() const {return "woof";}
void eat(std::string& food) const {food.clear();}
};
typedef shared_ptr<NativeDog> NativeDogPtr;
// C++/CLI wrapper class
ref class ManagedDog
{
NativeDogPtr* base_;
NativeDog& base() {return **base_;}
ManagedDog() {base_ = new NativeDogPtr(new NativeDog);}
~ManagedDog() {if (base_) delete base_;}
!ManagedDog() {delete this;}
property String^ name
{
String^ get() {return ToSystemString(base().name);}
void set(String^ name)
{
base().name = ToStdString(name);
/*** Do I need GC::KeepAlive(name) here? ***/
}
}
String^ bark() {return ToSystemString(base().bark());}
void eat(String^ food)
{
std::string nativeFood = ToStdString(food);
base().eat(nativeFood);
food = ToSystemString(nativeFood);
/*** Do I need GC::KeepAlive(food) here? ***/
}
};
// unmanaged C++ class
struct NativeKennel
{
vector<NativeDogPtr> dogs;
};
// C++/CLI wrapper class
ref class ManagedKennel
{
NativeKennel* base_;
NativeKennel& base() {return *base_;}
IList<ManagedDog^>^ dogs;
void addDog(ManagedDog^ dog)
{
base().dogs.push_back(*dog->base_);
dogs->Add(dog);
/*** Do I need GC::KeepAlive(dog) here? Will the IList manage the ManagedDog lifetimes? ***/
}
};
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是一种常见的故障模式,垃圾收集器看不到本机代码持有的任何引用。托管代码必须存储对委托本身的引用,以防止它被垃圾收集。有一个调试器助手,不知道为什么你没有看到它。有关更多详细信息,请参阅这篇 MSDN 库文章 。
This is a common failure mode, the garbage collector cannot see any reference held by native code. The managed code must store a reference to the delegate itself to prevent it from getting garbage collected. There's a debugger assistant for this, not sure why you didn't see it. More details in this MSDN Library article.
以上都不是!
如果您在 C++/CLI 中访问托管类,KeepAlive 将无济于事。您需要将数据固定在内存中,以阻止其在垃圾收集后重新定位。在所有这些示例中,这是由您调用的函数隐式完成的。
KeepAlive 有不同的目标。存储在堆栈上的引用将在最后一次取消引用对象后立即进行垃圾回收。 KeepAlive 通过将对象的生命周期延长到 KeepAlive 调用之后来防止这种情况发生。
None of the above!
If you access managed classes in C++/CLI, KeepAlive won't help. You need to pin the data in memory to stop it from relocating afer a garbage collect. In all of these examples, this is done implicitly by the functions you call.
KeepAlive has a different goal. References stored on the stack are subject to garbage collection immediately after the last time the object is dereferenced. KeepAlive prevents this from happening, by extending the lifetime of your object until after the KeepAlive call.