C++/CLI所支持的基本类型例如intdoublebool等在某些方面可以说是沿袭了ISOC++中的类型——同样的用法会在C++/CLI中得到同样的结果例如加法或者赋值操作但是C++/CLI也为这些基本类型引入了一些新的东西
在通用类型系统(CTS)中每一个基本类型都在System命名空间中存在一个对应的类(见表)例如int实际上完全等价于System::Int我们可以使用二者中的任何一个来声明一个整数
int ival = ;
Int ival = ;
出于移植性的考虑在使用这些基本类型时我们推荐大家使用内建的关键词而非System命名空间中的类名
表 基本类型和它们在System命名空间中对应的类对于System命名空间中类的公有静态成员我们既可以通过内建的关键字也可以通过System命名空间中的类名来访问例如为了获取一个数值类型的取值范围我们可以直接使用内建的关键字来访问其静态属性MaxValue和MinValue
int imaxval = int::MaxValue;
int iminval = Int::MinValue;
每个数值类型都支持一个名为Parse的成员函数用以将一个字符串转化为其所表示的数值例如给定下面的字符串
String^ bonus = $ ;
调用Parse会将myBonus初始化为
double myBonus = double::Parse( bonus ns );
其中ns表示对一些NumberStyles枚举类型取位或(bitwise or)运算的结果NumberStyles是位于System::Globalization命名空间中的一个枚举类型用于表征对空白货币符号小数点或者逗号等的处理看下面的代码
using namespace System;
using namespace System::Globalization;
double bonusString( String^ bonus )
{
NumberStyles ns = NumberStyles::AllowLeadingWhite;
ns |= NumberStyles::AllowCurrencySymbol;
ns |= NumberStyles::AllowThousands;
ns |= NumberStyles::AllowDecimalPoint;
return double::Parse( bonus ns );
}
我们也可以使用转型符号来在类型间进行显式的转换
int ival = ( int ) myBonus;
或者使用System::Convert类的一些转换方法例如ToDouble() ToInt() ToDateTime()等:
int ival = Convert::ToInt( myBonus );
两种转换方法采用的策略有所不同显式转型会直接对小数部分进行截断而Convert的成员函数则采用的是捨入算法例如上面的例子中ival赋值后的结果为而ival赋值后的结果为
我们还可以直接使用字面常量(literal)来调用其对应类型的成员函数虽然这乍看起来有些怪异例如我们可以编写如下代码
Console::Write( {} : ( )ToString() );
其中( )ToString()返回的是字面常量整数的字符串表示注意外面的圆括号是必须的因为它会使得编译器将后面的成员选择操作符点号绑定到整数上而不是将解析为一个double类型的字面常量——那样的话后面的ToString()将变得不合法为什么我们有时候需要这样做呢?一种可能的情况是将一个字符串传递给Console的成员函数要比传递实际的数值来的更加高效
对于字符以及字符串这样的字面常量我们也可以像上面的整数一样调用它们的成员函数但是它们的行为有一点点晦涩例如下面的代码
Console::WriteLine(( a )ToString() );
将在控制台上打印出而非a这个字符要将字符a打印出来我们需要将其首先转型为System::Char
Console::WriteLine(((wchar_t)a)ToString() );
C++/CLI对字符串字面常量采取了特殊的处理策略从某种程度上来讲字符串字面常量在C++/CLI中的类型更接近System::String而非C风格的字符串指针显然这将对重载函数的辨析产生影响例如
public ref class R {
public:
void foo( System::String^ ); // ()
void foo( std::string ); // ()
void foo( const char* ); // ()
};
void bar( R^ r )
{
// 调用哪一个foo呢?
r>foo( Pooh );
}
在ISOC++中这将被辨析为第个foo()因为字符串字面常量更接近const char*而非ISOC++标准库中的string类型但是在C++/CLI中上面的调用将被辨析为第个foo()因为现在字符串字面常量被认为更接近System::String而非字符指针要理解其中的缘由让我们往后退两步先来看看ISOC++和C++/CLI如何辨析一个重载函数然后再来看ISOC++和C++/CLI如何辨析一个字符串字面常量
一个重载函数的辨析过程通常包含以下三个步骤
.选择候选函数集合候选函数是指那些从词法范畴来看与所调用函数名相匹配的函数例如由于我们上面是在R的一个实例上调用foo()所以所有名称为foo但却不是R或者其基类的成员的那些函数将不被认为是候选函数这样看来我们现在有三个候选函数即R中三个名称为foo的成员函数如果这个阶段得到的候选函数集合为空那么调用将告失败
.从候选函数集合中选择可用函数集合可用函数是指函数声明时的参数个数和它们的类型与调用时所指定的相匹配的那些函数在我们上面的例子中三个候选函数都是可用函数如果这个阶段得到的可用函数集合为空那么调用也将失败
.从可用函数集合中选择最匹配的函数这个阶段将会对实际传递的参数和可用函数所声明的参数之间的转换进行一个排名对于只含一个参数的函数来说这个过程比较简单但是对于含有多个参数的函数来说这个过程就变得相对有些复杂如果没有一个最佳的匹配函数胜出那么调用将告失败也就是说各个可用函数的参数类型到实际参数类型之间的转换被认为一样的好换言之多个调用之间产生了混淆
那么现在摆在我们面前有两个问题()我们实际传递的参数Pooh到底是什么类型?()在判定类型转换的优劣时采用的是什么算法?
在ISOC++中字符串字面常量Pooh的类型为const char[]——注意在字符串字面常量后面有一个隐含的截断字符null在上面的例子中显然不存在这样的精确匹配因此必须应用某种形式的类型转换这样两个ISOC++候选函数()和()将进行竞争
void foo( std::string ); // ()
void foo( const char* ); // ()
那么编译器如何从中判断可用函数呢?C++语言对类型转换按照优先顺序定义有一个层级结构在这个结构中如果一种转换优于另一种转换那么它将被排在前面在C++/CLI中我们将CLI类型的行为也集成到了ISOC++的标准类型转换层级结构中下面是对集成之后的层级结构的一个描述
)精确匹配是最好的需要注意的是精确匹配并不意味着实际传递的参数类型和函数声明的形式参数类型完全匹配它们只需要足够接近就可以了我们下面将会看到足够接近对于ISOC++和C++/CLI中的字符串字面常量有着一些不同的含义
)在标准转换中拓宽转换要优于非拓宽转换例如将short拓宽为int要优于将int转换为double
)标准转换优于装箱(boxing)转换例如将int转换为double优于将int装箱为Object
)装箱转换优于用户自定义的隐式转换
)用户自定义的隐式转换优于没有任何转换!
)否则用户必须使用显式的转型符号来表示期望的转换
对于上面两个ISOC++下的候选函数将字符串字面常量转换为一个std::string属于上面第条即隐式调用string的构造器来创建一个临时string对象而将字符串字面常量转换为一个const char* 属于上面第条第条优于第条因此参数为const char*的那个函数在这场竞争中胜出
这种归属在第条精确匹配下的trivial conversions实际上在技术的定义上是很严格的总共有种这样的trivial conversions可以被归为精确匹配即使在这种trivial conversions中为了规范语言对类型的选择它们也有一个优先级的排序
大多数读者和程序员可能对于这样的细节没有多大兴趣并且通常情况下我们也无须深入到这些细节的地方但是如果我们要得到一个直观的语言行为并且确保它们在不同的实现上表现相同这些规则的存在就很有必要这是因为作为一门编程语言它的行为一般要具有某种程度的类型感知能力从而允许程序员忽略这些细节 下面让我们来对这种trivial conversions做一简单的了解其中种被称为左值转换(lvalue transformation)左值(lvalue)是一个可寻址的可被执行写操作的程序实体第种为限定性转换(qualification conversion)例如在一个类型声明上加一个const修饰符就属于这种转换其中种左值转换优于限定性转换
在我们上面的例子中由本地数组到指针的转换即由const char []到const char *就是一种左值转换在大多数情况下我们甚至不将这看作一种转换
这种形式的左值转换在C++/CLI中仍然适用但是在我们将System::String类引入之后字符串字面常量到const char*的转换就不再是最好的匹配了实际上在C++/CLI中Pooh这样的字符串字面常量的类型既是const char[