导读:本文向大家揭示了在将CLI类型系统和ISO-C++语义框架集成在一起的时候,微软做了哪些调整工作,以及如何在必要的时候调整在集成过程中所出现的各个情况的优先级。同时,这也提醒大家在将一个本地类型重新构造为一个CLI类的过程中需要注意的问题。
C++/CLI所支持的基本类型,例如int、double、bool等,在某些方面可以说是沿袭了ISO-C++中的类型——同样的用法会在C++/CLI中得到同样的结果,例如加法或者赋值操作。但是C++/CLI也为这些基本类型引入了一些新的东西。
在通用类型系统(CTS)中,每一个基本类型都在System命名空间中存在一个对应的类(见表1)。例如int实际上完全等价于System::Int32。我们可以使用二者中的任何一个来声明一个整数:
int ival = 0; Int32 ival2 = 0; | 出于移植性的考虑,在使用这些基本类型时,我们推荐大家使用内建的关键词,而非System命名空间中的类名。
| 基本类型 |
System命名空间中对应的类 |
注释/用法 |
| bool |
System::Boolean |
bool dirty = false; |
| char |
System::SByte |
char sp = ' '; |
| signed char |
System::SByte |
signed char ch = -1; |
| unsigned char |
System::Byte |
unsigned char ch = '\0'; |
| wchar_t |
System::Char |
wchar_t wch = ch; |
| short |
System::Int16 |
short s = ch; |
| unsigned short |
System::UInt16 |
unsigned short s = 0xffff; |
| int |
System::Int32 |
int ival = s; |
| unsigned int |
System::UInt32 |
unsigned int ui = 0xffffffff; |
| long |
System::Int32 |
long lval = ival; |
| unsigned long |
System::UInt32 |
unsigned long ul = ui; |
| long long |
System::Int64 |
long long etime = ui; |
| unsigned long long |
System::UInt64 |
unsigned long long mtime = etime; |
| float |
System::Single |
float f = 3.14f; |
| double |
System::Double |
double d = 3.14159; |
| long double |
System::Double |
long double d = 3.14159L; | 表1 基本类型和它们在System命名空间中对应的类
对于System命名空间中类的公有静态成员,我们既可以通过内建的关键字,也可以通过System命名空间中的类名来访问。例如,为了获取一个数值类型的取值范围,我们可以直接使用内建的关键字来访问其静态属性MaxValue和MinValue。
int imaxval = int::MaxValue; int iminval = Int32::MinValue; | 每个数值类型都支持一个名为Parse的成员函数,用以将一个字符串转化为其所表示的数值。例如,给定下面的字符串:
| String^ bonus = "$ 12,000.79"; | 调用Parse会将myBonus初始化为12000.79:
| 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(), ToInt32(), ToDateTime()等:
| int ival2 = Convert::ToInt32( myBonus ); | 两种转换方法采用的策略有所不同:显式转型会直接对小数部分进行截断,而Convert的成员函数则采用的是舍入算法。例如上面的例子中ival赋值后的结果为12000,而ival2赋值后的结果为12001。
我们还可以直接使用字面常量(literal)来调用其对应类型的成员函数,虽然这乍看起来有些怪异。例如,我们可以编写如下代码:
| Console::Write( "{0} : ", ( 5 ).ToString() ); | 其中( 5 ).ToString()返回的是字面常量整数5的字符串表示。注意5外面的圆括号是必须的,因为它会使得编译器将后面的成员选择操作符点号绑定到整数5上,而不是将'5.'解析为一个double类型的字面常量——那样的话,后面的ToString()将变得不合法。为什么我们有时候需要这样做呢?一种可能的情况是将一个字符串传递给Console的成员函数要比传递实际的数值来的更加高效。
对于字符以及字符串这样的字面常量,我们也可以像上面的整数一样调用它们的成员函数,但是它们的行为有一点点晦涩。例如,下面的代码:
| Console::WriteLine(( 'a' ).ToString() ); | 将在控制台上打印出97,而非'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^ ); // (1) void foo( std::string ); // (2) void foo( const char* ); // (3) };
void bar( R^ r ) { // 调用哪一个foo呢? r->foo( "Pooh" ); } |
|
|