Array 和 WriteOnlyArray (C++/CX)

可以在 C++/CX 程序中自由使用常规 C 样式数组或 std::array(尽管 std::vector 通常是更好的选择),但是,在元数据内发布的任何 API 中,必须将 C 样式数组或向量转换为 Platform::ArrayPlatform::WriteOnlyArray 类型,具体情况取决于如何使用它。 Platform::Array 类型既不像 std::vector 那样高效也不像它那样功能强大,因此,一般原则是,应避免在对数组元素执行大量操作的内部代码中使用该类型。

以下数组类型可在 ABI 中传递:

  1. const Platform::Array^

  2. Platform::Array^*

  3. Platform::WriteOnlyArray

  4. Platform::Array^ 的返回值

使用这些数组类型可实现由 Windows 运行时定义的三类数组模式。

PassArray
在调用方将数组传递给方法时使用。 C++ 输入参数类型是 const Platform::Array<T>。

FillArray
在调用方传递一个数组以便方法填充时使用。 C++ 输入参数类型是 Platform::WriteOnlyArray<T>。

ReceiveArray
在调用方接收方法分配的数组时使用。 在 C++/CX 中,你可以在返回值中将数组返回为 Array^,也可以将其作为类型 Array^* 的输出参数返回。

PassArray 模式

当客户端代码将数组传递给 C++ 方法,而该方法没有修改它时,该方法接受该数组作为 const Array^。 在 Windows 运行时应用程序二进制接口 (ABI) 级别,这称为 PassArray。 下一个示例演示如何将 JavaScript 中分配的数组传递给读取它的 C++ 函数。

//JavaScript
function button2_click() {
    var obj = new JS-Array.Class1();
    var a = new Array(100);
    for (i = 0; i < 100; i++) {
        a[i] = i;
    }
    // Notice that method names are camelCased in JavaScript.
    var sum = obj.passArrayForReading(a);
    document.getElementById('results').innerText
        = "The sum of all the numbers is " + sum;
}

下面的代码片段演示 C++ 方法:

double Class1::PassArrayForReading(const Array<double>^ arr)
{
    double sum = 0;
    for(unsigned int i = 0 ; i < arr->Length; i++)
    {
        sum += arr[i];
    }
    return sum;
}

ReceiveArray 模式

ReceiveArray 模式中,客户端代码声明一个数组,然后将数组传递给为其分配内存的方法并初始化它。 C++ 输入参数类型为指向乘幂号的指针:Array<T>^*。 下面的示例演示如何在 JavaScript 中声明一个数组对象并将其传递给一个 C++ 函数,该函数分配内存,初始化元素,并将其返回到 JavaScript。 JavaScript 将分配的数组视为返回值,而 C++ 将其视为输出参数。

//JavaScript
function button3_click() {
    var obj = new JS-Array.Class1();

    // Remember to use camelCase for the function name.
    var array2 = obj.calleeAllocatedDemo2();
    for (j = 0; j < array2.length; j++) {
        document.getElementById('results').innerText += array2[j] + " ";
    }
}

下面的代码片段演示实现 C++ 方法的两种方式:


// Return array as out parameter...
void Class1::CalleeAllocatedDemo(Array<int>^* arr)
{
    auto temp = ref new Array<int>(10);
    for(unsigned int i = 0; i < temp->Length; i++)
    {
        temp[i] = i;
    }

    *arr = temp;
}

// ...or return array as return value:
Array<int>^ Class1::CalleeAllocatedDemo2()
{
    auto temp = ref new Array<int>(10);    
    for(unsigned int i = 0; i < temp->Length; i++)
    {
        temp[i] = i;
    }

    return temp;
}

填充数组

若要在调用方分配一个数组,并在被调用方初始化或修改它,请使用 WriteOnlyArray。 下一个示例演示如何实现使用 WriteOnlyArray 的 C++ 函数并从 JavaScript 调用它。

// JavaScript
function button4_click() {
    var obj = new JS-Array.Class1();
    //Allocate the array.
    var a = new Array(10);

    //Pass the array to C++.
    obj.callerAllocatedDemo(a);

    var results = document.getElementById('results');
    // Display the modified contents.
    for (i = 0; i < 10; i++) {
        document.getElementById('results').innerText += a[i] + " ";
    }
}

下面的代码片段演示实现 C++ 方法的方式:

void Class1::CallerAllocatedDemo(Platform::WriteOnlyArray<int>^ arr)
{
    // You can write to the elements directly.
    for(unsigned int i = 0; i < arr->Length; i++)
    {
        arr[i] = i;
    }   
}

数组转换

以下示例演示如何使用 Platform::Array 构造其他类型的集合:

#include <vector>
#include <collection.h>
using namespace Platform;
using namespace std;
using namespace Platform::Collections;

void ArrayConversions(const Array<int>^ arr)
{
    // Construct an Array from another Array.
    Platform::Array<int>^ newArr = ref new Platform::Array<int>(arr);

    // Construct a Vector from an Array
    auto v = ref new Platform::Collections::Vector<int>(arr); 

    // Construct a std::vector. Two options.
    vector<int> v1(begin(arr), end(arr));
    vector<int> v2(arr->begin(), arr->end());

    // Initialize a vector one element at a time.
    // using a range for loop. Not as efficient as using begin/end.
    vector<int> v3;
    for(int i : arr)
    {
        v3.push_back(i);
    }   
}

下一示例演示如何根据 C 样式数组构造 Platform::Array 并通过公共方法返回它。

Array<int>^ GetNums()
{
    int nums[] = {0,1,2,3,4};
    //Use nums internally....

    // Convert to Platform::Array and return to caller.
    return ref new Array<int>(nums, 5);
}

交错数组

Windows 运行时类型系统不支持交错数组的概念,因此无法在公共方法中将 IVector<Platform::Array<T>> 作为返回值或方法参数传递。 要跨 ABI 传递交错数组或一系列序列,请使用 IVector<IVector<T>^>

使用 ArrayReference 可避免复制数据

在某些情况下,比如,数据将通过 ABI 传递给 Platform::Array,并且最终需要在 C 样式数组中处理数据以实现较高的效率,可以使用 Platform::ArrayReference 来避免额外的复制操作。 在将 Platform::ArrayReference 作为实参传递给采用 Platform::Array 的形参时,ArrayReference 会直接将数据存储到你指定的 C 样式数组中。 需要了解的是, ArrayReference 未锁定源数据,因此,如果在调用完成之前在另一个线程上修改或删除此数据,则结果将是未定义的。

下面的代码片段演示如何将 DataReader 操作的结果复制到 Platform::Array(通常模式)中,以及如何替代 ArrayReference 以将数据直接复制到 C 样式数组中:

public ref class TestReferenceArray sealed
{
public:

    // Assume dr is already initialized with a stream
    void GetArray(Windows::Storage::Streams::DataReader^ dr, int numBytesRemaining)
    {
        // Copy into Platform::Array
        auto bytes = ref new Platform::Array<unsigned char>(numBytesRemaining);            

        // Fill an Array.
        dr->ReadBytes(bytes);

        // Fill a C-style array
        uint8 data[1024];
        dr->ReadBytes( Platform::ArrayReference<uint8>(data, 1024) );
    }
};

避免将数组公开为属性

通常,应避免将 Platform::Array 类型公开为 ref 类中的属性,因为将返回整个数组,即使在客户端代码仅尝试访问单个元素时也是如此。 当需要将序列容器公开为公共 ref 类的属性时,Windows::Foundation::IVector 是更好的选择。 在私有或内部 API 中(未发布到元数据),请考虑使用标准 C++ 容器(如 std::vector)。

另请参阅

类型系统
C++/CX 语言参考
命名空间参考