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