在本文中,我们将介绍静态成员函数以及 Windows 运行时如何支持它们。Windows 运行时引用类型,在 C++/CX 中也称为 ref 类或运行时类可以具有静态成员函数。在 C++/CX 中,用于在运行时类中声明静态成员函数的语法与在普通 C++ 类中使用的语法完全相同。为了演示这一点,下面是一个具有一个静态成员函数的运行时类:
public ref class KnownValues sealed
{
public:
static int GetZero() { return 0; }
private:
KnownValues(); // This type can't be constructed
};
请注意,我们已声明私有默认构造函数,以确保无法创建此类的实例。如果我们定义 ref 类并且不声明任何构造函数,则编译器将为该类型提供一个公共默认构造函数,就像为普通 C++ 类一样。可以定义一个可构造且具有静态成员的类型;我们只是将此类型设为不可构造,以使下一个示例更简单一些。
同样,用于调用运行时类声明的静态成员函数的语法与普通 C++ 语法完全相同。以下是我们调用 GetZero 的方式:
int x = KnownValues::GetZero();
因此,至少从语法上讲,C++/CX 中的静态成员函数没有什么特别之处。但是,Windows 运行时支持静态成员函数的机制值得一提。
对静态成员函数的调用独立于声明该函数的类的任何实例。静态成员函数没有 this 指针。我们不需要创建 KnownValues 对象来调用其 GetZero 静态成员函数。为了允许运行时类具有静态成员函数,我们需要某种方法,允许我们在不先创建其声明类型的实例的情况下调用函数。
通过以下方式实现了对构造函数的支持:
激活工厂允许我们实现与运行时类关联的函数,这些函数无需先创建该运行时类的实例即可调用。特定运行时类只能有一个与之关联的激活工厂,但该激活工厂可以实现多个接口。除了实现零个或多个工厂接口声明构造函数之外,激活工厂还可以实现零个或多个静态接口或者声明静态成员函数。
我们将使用 C++ 和 WRL 重新实现 KnownValues 类型,但我们不会讲得太详细,激活工厂和这里没有太多区别。首先,下面是运行时类及其静态接口的 IDL 声明,它们非常简单:
[exclusiveto(KnownValues)]
[uuid(ca8c9b14-f2a3-4f1e-aa50-49bfa3a5dbd3)]
[version(1.0)]
interface IKnownValuesStatics : IInspectable
{
HRESULT GetZero([out] [retval] int* value);
}
[static(IKnownValuesStatics, 1.0)]
[version(1.0)]
runtimeclass KnownValues
{
}
KnownValues 上的静态属性指定 IKnownValueStatics 接口是 KnownValues 运行时类的静态接口。请注意,KnownValues 类型未声明它实现任何实例接口(即其主体为空)。这是因为永远不会创建 KnownValues 运行时类的任何实例。此运行时类实际上只是用于定义静态成员函数的容器,在 C# 术语中,这称为静态类。
激活工厂实现也很简单:
class KnownValuesFactory : public ActivationFactory<IKnownValuesStatics>
{
InspectableClassStatic(RuntimeClass_WRLKnownValuesComponent_KnownValues, BaseTrust)
public:
STDMETHODIMP GetZero(int* value) override
{
*value = 0;
return S_OK;
}
};
ActivatableStaticOnlyFactory(KnownValuesFactory)
请注意,由于我们永远不会创建 KnownValues 的实例,因此我们实际上不需要在 C++ 中定义 KnownValues 类。我们只需要定义激活工厂,它实现 IKnownValueStatics 静态接口。
所有激活工厂还必须实现 IActivationFactory 接口。我们使用的 ActivationFactory 基类模板提供了此接口的默认实现,它对不可激活类型执行正确的操作。特定的运行时类可能既可激活又具有静态成员函数。在这种情况下,其激活工厂将同时实现工厂接口和静态接口。
由于静态成员函数的实现方式与构造函数相同,因此调用静态成员函数的过程与调用构造函数的过程完全相同也就不足为奇了。需要两个步骤:首先,我们需要获取该类型的激活工厂,然后我们就可以调用该函数。调用 GetZero 的 WRL 代码如下:
HStringReference classId(RuntimeClass_WRLKnownValuesComponent_KnownValues);
ComPtr<IKnownValuesStatics> statics;
RoGetActivationFactory(
classId.Get(),
__uuidof(IKnownValuesStatics),
reinterpret_cast<void**>(statics.GetAddressOf()));
int x = 0;
statics->GetZero(&x);
除了为简洁起见省略的错误处理之外,此代码等效于上面的 C++/CX 对 GetZero 的调用:
int x = KnownValues::GetZero();