c++ /CX是否检测和求解对象的循环?

Does C++/CX detect and solve cycles of objects?

本文关键字:对象 循环 CX 是否 检测 c++      更新时间:2023-10-16

根据我对c++/CX的理解,它不使用垃圾收集,而是使用引用计数方法。

引用计数的问题是它不能处理循环。循环通常使用弱引用来解决,例如标准c++中的weak_ptr。

但是我无法在c++/CX中找到显式指定弱引用的方法。因此,我认为这是由c++/CX本身处理的。我想知道c++/CX将如何解决这个问题。

例如,看看下面的代码:

ref class Foo
{
public:
    Bar^ bar;
};
ref class Bar
{
public:
    Foo^ foo;
};
ref class App
{
public:
    virtual void OnLaunched(LaunchActivatedEventArgs^ args)
    {
        Foo^ foo = ref new Foo();
        Bar^ bar = ref new Bar();
        foo.bar = bar;
        bar.foo = foo;
    }
};

c++/CX如何检测这个循环?

c++/CX如何解决这个循环?

c++/CX如何决定这些对象中的哪一个应该是"根对象",哪一个应该是"弱引用"?

简短的回答:不,c++/CX不能检测和解决对象的循环。

长话短说:WinRT本身有一个弱引用的标准机制。在ABI级别上,这是根据接口IWeakReferenceIWeakReferenceSource定义的,您可以在"%ProgramFiles%Windows Kits8.0IncludewinrtWeakReference.idl"中看到。

在c++/CX中,所有的类都将自动实现IWeakReferenceSource,因此它们的所有实例都可以弱引用。要获取和存储对对象的弱引用,应该使用帮助器类Platform::WeakReference(在vcclib .h中定义):

Foo^ foo = ref new Foo;
Platform::WeakReference weakRef(foo);
使用Resolve<T>: 来检索对象。
foo = weakRef.Resolve<Foo>();

像往常一样,如果对象已经被销毁,您将得到nullptr

除此之外,WeakReference实例的行为或多或少像一个智能指针——它可复制、可移动、可比较、可从nullptr赋值、可隐式转换为未指定的bool类型等。

请注意,从VS11 Beta开始,如果你试图使用WeakReference, IDE智能感知将会阻止它,用潦草的划线等。尽管如此,编译器仍然可以很好地处理它们。

查看在SDK中包含winrtWeakReference.h。它定义了可用于此目的的IWeakReference。

它将沿用旧的COM编程方式,手工思考并添加显式的decref调用。

正如Pavel Minaev所说,WinRT有一个弱引用的标准机制:IWeakReferenceSource/IWeakReference接口,WRL::WeakRef助手类,等等。

不幸的是,通过ref class定义的类不实现IWeakReferenceSource,至少在这个开发人员预览版本中,我找不到任何方法来添加这个接口。

一个可能的解决方法是在"本地"c++中实现WinRT类,而不使用c++/CX扩展。WRL框架大大简化了这项任务(它为WinRT做了ATL为COM做的事情)。

有一个WinRT示例("DLL服务器创作"示例),它展示了如何在不使用ref的情况下实现WinRT对象。默认情况下,从WRL::RuntimeClass<Interface>继承的类自动实现IWeakReferenceSource,因此提供弱引用。

Mozilla XPCOM实现了Bacon的方法,如果需要,这个方法在某种程度上可以移植到WinRT。一般来说,避免跟踪垃圾收集器是一件好事。开发人员仍然有很多方法可以泄漏内存,所以最好不要陷入错觉。此外,从控制的角度来看,循环所有权没有意义。这就像蒙乔森抓着自己的头发把自己从泥潭里拽出来让自己漂浮在空中。每一个对象的存在都必须有一个理由,引用计数就是这个理由的表现。另一种表现形式是修改权,它产生了高度依赖于引用计数器可用性的健壮的写时复制技术。在只有跟踪垃圾收集的环境中,要么必须执行可变数据结构的深度拷贝,以防止不必要的突变,要么使用不可变数据结构,对小的深度更改会造成很大的损失。或者来回转换可变和不可变数据结构。此外,据估计,跟踪垃圾收集只有在需要5倍的可用RAM时才能正常工作(不考虑深度复制副本)。相比之下,保守的分配器使用所需RAM的2倍(由于碎片而浪费)。不要被欺骗了,复制垃圾收集器只会使分配更快,但是要达到相同的性能,它比引用计数的保守垃圾收集器浪费2.5倍的RAM。

看这个苹果。他们将TGC作为一个可选特性引入Objective-C 2.0,但后来又弃用了它,大量的iPhone应用程序没有它也能运行得很好。iphone以出色的用户体验和较长的电池充电时间而闻名。Windows 10在我4Gb内存的电脑上像地狱一样死机,而Mac OS X 10.4.10 Hackintosh在1Gb内存上运行得相当顺利。也许这之间有某种联系,你不这么认为吗?也许内存在某个地方不小心泄露了,但与冻结和巨大的内存消耗相比,最终很难观察到。

巨大的RAM消耗使程序交换到磁盘,如果它们交换到磁盘,然后开始跟踪垃圾收集,交换的页面都返回到RAM,并且将交换的页面移回RAM非常慢。此外,在这样做时,必须抢占其他应用程序的页面以交换文件。正如我们所知,跟踪垃圾收集的应用程序使用2.5倍的RAM,因此这些应用程序有2.5倍的机会进行交换。突然间,另一个应用程序也将启动垃圾收集,并且必须将交换的页面返回RAM,抢占其他应用程序的页面。它就像永续的移动,反之亦然。正常的永动机无限地从稀薄的空气中产生能量,而永动机反过来无限地浪费能量。跟踪垃圾收集是一种永不结束的算法。它时不时地被启发式地启动,只有当它完成时,如果运气好,它才知道。也许这次我们会幸运地收集到一些东西,也许是第二次,也许是第三次。你离开PC很长一段时间,希望它最终会完成它的业务,最终让你工作,但这个业务永远不会结束。突然,两个应用程序同时运行跟踪垃圾收集,并开始争夺未交换的RAM。跟踪垃圾收集可能会对同一页进行多次后续访问,因此一个页可以多次往返交换。在办公环境中,只有老板的个人电脑可能有很多内存,其他个人电脑尽可能便宜。此外,杀毒软件被强制部署到每台办公电脑上,办公室员工无法摆脱它。防病毒程序为内存签名保留RAM,使其更加稀缺,并且还检查每个I/O,包括交换文件驱动冻结,以完成疯狂。那才是人间地狱。

我问过跟踪垃圾收集器的倡导者,他们是否能像我一样观察到死机,结果证明他们在pc上投入了大量内存(比如笔记本电脑上的16Gb内存!!),在单用户模式下使用,垃圾收集对他们来说很好。在地狱里,他们将不得不在最便宜的办公电脑上进行开发,并强制部署防病毒软件。

所以我建议你不要只看循环收集问题。学会热爱引用计数,享受它,让用户享受你的瘦身程序。使用大多数引用计数。用于嵌套结构的健壮的写时复制。具有包装连接的数据库连接池,当连接的包装器不再被引用时,连接将立即返回到连接池。网络的透明度。RAII .

如果你真的没有其他选择,借鉴Mozilla XPCOM。顺便说一下,在Windows操作系统上,Mozilla XPCOM被告知具有与Microsoft COM相同的ABI,但不确定。

那些不是WinRT对象,那些是你自定义类型的对象。因为你已经使用c++/CLI语法将它们声明为。net引用类型(ref class),它们像所有。net引用类型一样被垃圾收集,通过可达性测试,而不是引用计数。

Win32对象一直是引用计数的,所以WinRT似乎没有改变任何东西。它只是为您提供了c++ RAII类,在Win32下程序员编写了自己的包装器来利用RAII。