Standard ECMA-372 1st Edition / December 2005
C++/CLI Language Specification
C++/CLI 语言详述
译者:Enzo Yang
8.3 参数
一个参数数组(parameter array)是一种到以省略号结尾的参数列表的类型安全转换.
一个参数数组以…符号开头,紧跟着就是CLI的数组类型.一个函数中只能使用一个参数数组, 而且参数数组必须是最后一个参数. 参数数组只能是单维CLI数组. 函数调用者可以传递一个或任何个数特定同样的变量给参数数组. 下面是例子.
void F (…array<int>^ args) {
Console::WriteLine(“# of arguments: {0}”, args->Length);
for (int i = 0; i < args->Length; i++)
Console::WriteLine(“/targs[{0}] = {1}”, i , args[i]);
}
int main() {
F();
F(1);
F(1, 2);
F(1, 2, 3);
F(gcnew array<int> {1, 2, 3, 4});
}
这里显示了可以处理不同个数int参数的函数F, 和几个对F的调用, 输出如下.
# of arguments: 0
# of arguments: 1
args[0] = 1
# of arguments: 2
args[0] = 1
args[1] = 2
#of arguments: 3
args[0] = 1
args[1] = 2
args[2] = 3
# of arguments: 4
args[0] = 1
args[1] = 2
args[2] = 3
args[3] = 4
如果把参数数组定义为一个装有System::Object^的CLI数组的话, 那么可以向它传递不同类型的参数; 比如说:
void G (…array<Object^>^ args) { … }
G(10, “hello”, 1.23, ‘X’); // arguments 1 , 3 , and 4 are boxed
这本标准中有好多例子都用了Console类的WriteLine函数. 它调用参数的行为就像下面演示的那样
int a = 1, b = 2;
Console::WriteLine(“a = {0}, b = {1}”, a , b);
它是用参数数组来实现的. Console类为WriteLine提供了几种重载版本,一部分用来处理那些传来很少参数的情况, 另一部分是用参数数组处理不定个数参数的情况.
namespace System {
public ref class Object { … };
public ref class String { … };
public ref class Console {
public:
static void WriteLine(String^ s) { … }
static void WriteLine(String^ s, Object^ a) { … }
static void WriteLine(String^ s, Object^ a, Object^ b) { … }
static void WriteLine(String^ s, Object^ a, Object^ b, Object^ c)
{ … }
…
static void WriteLine(String^ s, …array<Object^>^ args) { … }
};
}
CLI库使用C#的语法来显示库里面的函数, C#的关键字params表示一个参数数组. 比如, 上面WriteLine函数的声明用C#写是这样的:
public static void WriteLine(string s, params object[] args)
8.4 自动的内存管理
public ref class Stack {
public:
Staci() {
first = nullptr;
}
property bool IsEmpty {
bool get() {
return (first == nullptr);
}
}
Object^ Pop() {
if (first == nullptr)
throw gcnew Exception(“Can’t Pop from an empty Stack.”);
else {
Object^ temp = first->Value;
first = first->Next;
return temp;
}
}
void Push(Object^ o) {
first = gcnew Node( o , first);
}
ref struct Node {
Node^ Next;
Object^ Value;
Node(Object^ value) {
Next = nullptr;
Value = value;
}
Node(Object^ value, Node^ next) {
Next = next;
Value = value;
}
};
private:
Node^ first;
};
这个例子显示了一个用链表实现的Stackl类. Node实例是在调用Push函数时创建的, 当不要用时垃圾收集器就会把它回收. 当一个Node实例不再可能被任何代码得到时它就可以被回收了. 比如说, 当一个节点从Stack中清除后, 这个节点就具备了被回收的条件.
int main() {
Stack^ s = gcnew Stack;
for (int i = 0 ; i < 10; i++)
s->push(i);
s = nullptr;
}
这个例子展示了一段使用Stack类的代码. 这个Stack在被创建之初压入了10个元素, 然后把它的句柄赋值为nullptr. 一旦变量s被设置为空值, 这个Stack连同那十个Node都具备了被回收的条件. 垃圾收集器可以马上把垃圾清除,但没有必要马上.
C++/CLI的垃圾收集器可以移动CLI堆中的objects, 但是这是让大多数开发者都看不到的. 对于开发者来说自动的内存管理已经足够了, 但是有时需要控制它来提高一点点性能, C++/CLI提供一个钉住(pin)object在CLI堆中的功能, 通过这样来暂时防止垃圾收集器移动那个object.
void f(int* p) { *p = 100;}
int main() {
array<int>^ arr = gcnew array<int>(100);
pin_ptr<int> pinp = &arr[0]; //pin arr’s location
f(pinp); //change arr[0]’s value
}
8.5 表达式
C++/CLI加强了标准C++操作符方面功能. 比如:
# 新增的delegate需要用函数的方式来调用封装在delegate中的函数 (注:对delegate的解释在后面,现在只要知道它装有一组同样形式的函数,并可以对它里面的函数同时调用就行.)
# 添加了typeid的用途. 比如说, Int32::typeid返回一个指向System::Type对象的句柄, 这个对象描述了CLI的Int32类型.
# 加强了转换操作符来适应句柄类型.
# 添加了safe_cast操作符.
# 添加了操作符gcnew. 它用来分配CLI堆中的内存.
# 加强了 + 和 - 操作符来分别实现delegate的添加和删除.
# 加强了简单赋值操作来适应左边运算域是property和event的情况.
# 复合赋值操作符被综合到对应的位运算符.(Compoound assignment operators are synthesized from the corresponding binary operator)
8.6 语句
新增了for each语句. 这个语句迭代一个集合中的元素, 先后对集合里面的元素执行一个程序块中的代码.
void dispay(array<int>^ args) {
for each ( int i in args)
Console::WriteLine(i);
}
一个类如果它实现了System::Collection::IEnumerable接口或者通过一些规则实现了一集合样式(collection pattern), 那么它就可以称为集合类型(collection type).