windows C++-通过 C++/WinRT 使用 API(二)

延迟初始化

在 C++/WinRT 中,每个类型都有一个特殊的 C++/WinRT std::nullptr_t 构造函数。 除了该构造函数,所有其他类型的构造函数(包括默认的构造函数)都会导致系统创建一个支持的 Windows 运行时对象,并为你提供它的智能指针。 因此,该规则适用于使用默认构造函数的任何地方,例如未初始化的本地变量、未初始化的全局变量以及未初始化的成员变量。

另一方面,如果你想要构造实现类型的变量,而无需它反过来构造支持的 Windows 运行时对象(以便你可以延迟该工作),你可以这样做。 使用该特殊 C++/WinRT std::nullptr_t 构造函数(C++/WinRT 的实现已将它插入每个运行时类中)声明你的变量或字段。 在下面的代码示例中,我们将该特殊构造函数与 m_gamerPicBuffer 配合使用。

#include <winrt/Windows.Storage.Streams.h>
using namespace winrt::Windows::Storage::Streams;

#define MAX_IMAGE_SIZE 1024

struct Sample
{
    void DelayedInit()
    {
        // Allocate the actual buffer.
        m_gamerPicBuffer = Buffer(MAX_IMAGE_SIZE);
    }

private:
    Buffer m_gamerPicBuffer{ nullptr };
};

int main()
{
    winrt::init_apartment();
    Sample s;
    // ...
    s.DelayedInit();
}

 除 std::nullptr_t 构造函数以外的实现类型上的所有构造函数都将导致创建后备 Windows 运行时对象。 std::nullptr_t 构造函数本质上不执行任何操作。 它预期的实现对象会在后续时间初始化。 因此,不论运行时类是否具有默认的构造函数,你都可以使用此技巧实现有效的延迟初始化。

此注意事项会影响在其中调用默认构造函数的其他位置(如在向量和映射中)。 请考虑此代码示例,对它需要“空白应用(C++/WinRT)” 项目

std::map<int, TextBlock> lookup;
lookup[2] = value;

// 进行分配会创建新 TextBlock ,然后立即使用 value 覆盖它。 下面是补救措施。

std::map<int, TextBlock> lookup;
lookup.insert_or_assign(2, value);
不要错误地延迟初始化

请注意不要错误地调用 std:: nullptr_t 构造函数。 编译器的冲突解决偏向于它而非工厂构造函数。 例如,请考虑这两个运行时类定义。

// GiftBox.idl
runtimeclass GiftBox
{
    GiftBox();
}

// Gift.idl
runtimeclass Gift
{
    Gift(GiftBox giftBox); // You can create a gift inside a box.
}

假设我们想要构造一个不在盒子内的 Gift(使用未初始化的 GiftBox 构造的 Gift)。 首先,让我们看看错误 的做法。 我们知道有一个接受 GiftBox 的 Gift 构造函数。 但是,如果想要传递 null GiftBox(通过统一初始化调用 Gift 构造函数,如下所示),则不会获得我们想要的结果 。

// These are *not* what you intended. Doing it in one of these two ways
// actually *doesn't* create the intended backing Windows Runtime Gift object;
// only an empty smart pointer.

Gift gift{ nullptr };
auto gift{ Gift(nullptr) };

// 在此处得到的是一个未初始化的 Gift。 你无法通过未初始化的 GiftBox 得到 Gift。 
// 下面是正确 的做法。

// Doing it in one of these two ways creates an initialized
// Gift with an uninitialized GiftBox.

Gift gift{ GiftBox{ nullptr } };
auto gift{ Gift(GiftBox{ nullptr }) };


// 在不正确的示例中,传递 nullptr 文本会以有利于延迟初始化构造函数的方式解析。 
// 若要以有利于工厂构造函数的方式解析,参数的类型必须是 GiftBox。 
// 仍然可以选择传递一个显式延迟初始化 GiftBox,如正确的示例中所示。

// 下一个示例也正确 ,因为参数的类型为 GiftBox,而不是 std:: nullptr_t。

GiftBox giftBox{ nullptr };
Gift gift{ giftBox }; // Calls factory constructor.

// 仅当传递 nullptr 文本时才会引起多义性。
 不要错误地复制构造。

此警告类似于上面的不要错误地延迟初始化部分中所述的警告。

除了该延迟初始化构造函数之外,C++/WinRT 实现类也会将复制构造函数注入到每个运行时类中。 它是一个单参数构造函数,接受与所构造对象相同的类型。 生成的智能指针指向其构造函数参数指向的同一后备 Windows 运行时对象。 结果是两个智能指针对象指向同一后备对象。

下面是我们将在代码示例中使用的运行时类定义。

// GiftBox.idl
runtimeclass GiftBox
{
    GiftBox(GiftBox biggerBox); // You can place a box inside a bigger box.
}

假设我们想要在一个较大的 GiftBox 内构造 GiftBox。

GiftBox bigBox{ ... };

// These are *not* what you intended. Doing it in one of these two ways
// copies bigBox's backing-object-pointer into smallBox.
// The result is that smallBox == bigBox.

GiftBox smallBox{ bigBox };
auto smallBox{ GiftBox(bigBox) };

正确的做法是显式调用激活工厂。

GiftBox bigBox{ ... };

// These two ways call the activation factory explicitly.

GiftBox smallBox{
    winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };
auto smallBox{
    winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };
如果在 Windows 运行时组件中实现此 API

无论你是自行创作该组件,还是该组件来自供应商,本部分均适用。

有关安装和使用 C++/WinRT Visual Studio 扩展 (VSIX) 和 NuGet 包(两者共同提供项目模板,并生成支持)的信息,请参阅适用于 C++/WinRT 的 Visual Studio 支持。

在应用程序项目中,引用 Windows 运行时组件的 Windows 运行时元数据 (.winmd) 文件,然后生成。 在生成过程中,cppwinrt.exe 工具生成标准 C++ 库,该库全面描述(或实现)该组件的 API 接口。 换言之,生成的库包含该组件的具现类型。

然后,与 Windows 命名空间类型一样,你只需包含标头并通过其构造函数之一构造实现的类型。 应用程序项目的启动代码注册运行时类,然后具现的类型的构造函数调用 RoActivateInstance 来激活引用组件中的运行时类。

#include <winrt/ThermometerWRC.h>

struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
    ThermometerWRC::Thermometer thermometer;
    ...
};
如果在使用的项目中实现 API

本部分中的代码示例摘自 XAML 控件;绑定到 C++/WinRT 属性主题。 请查看该主题了解更多详细信息、代码和演练,其中该演练描述了使用在采用它的同一项目中实现的运行时类的情况。

通过 XAML UI 使用的类型必须为运行时类,即使其位于与 XAML 相同的项目中。 在这种情况下,从运行时类的 Windows 运行时元数据 (.winmd) 中生成一个具现类型。 同样,你还可包含一个标头,但随后可选择 C++/WinRT 版本 1.0 或版本 2.0 方法来构造该运行时类的实例。 版本 1.0 方法使用 winrt::make;版本 2.0 方法被称作统一构造。 让我们来逐一查看。

使用 winrt::make 进行构造

让我们从默认方法(C++/WinRT 版本 1.0)开始,因为最好至少要熟悉该模式。 通过其 std::nullptr_t 构造函数构造具现类型。 该构造函数不执行任何初始化,所以你接下来必须通过 winrt::make 帮助程序函数向该实例分配一个值,同时传递任何必要的构造函数参数。 在使用代码的同一项目中实现的运行时类无需进行注册,且无需通过 Windows 运行时/COM 激活进行实例化。

// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        BookstoreViewModel MainViewModel{ get; };
    }
}

// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
    ...
    private:
        Bookstore::BookstoreViewModel m_mainViewModel{ nullptr };
};
...

// MainPage.cpp
...
#include "BookstoreViewModel.h"

MainPage::MainPage()
{
    m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();
    ...
}
  • 20
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值