科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件解析C++/CLI之头文件、内联函数与数组

解析C++/CLI之头文件、内联函数与数组

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

在传统C++的设计与实现中,你可对需建模的每种类型进行定义,并把定义放在各自的头文件中。

作者:谢启东编译 来源:天极开发 2007年11月14日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
Equals函数 PK ==操作符

  对一个引用类而言,相等性比较是通过一个名为Equals的函数,而不是重载 == 操作符来实现的。但是,还是可以重载此操作符的,见例2。

  例2:

public ref class Point
{
 ...
 static bool operator==(Point^ p1, Point^ p2)
 {
  Object^ o1 = p1;
  Object^ o2 = p2;
  /*1*/ if (o1 == nullptr || o2 == nullptr)
  {
   return false;
  }
  if (o1 == o2) //在测试自身吗?
  {
   return true;
  }
  if (p1->GetType() == p2->GetType())
  {
   return (p1->X == p2->X) && (p1->Y == p2->Y);
  }
  return false;
 }
};

  在标号1中,指明了不接受空值的句柄,但是,此处如果用p1与p2来代替o1与o2,就会造成自身的递归调用,因此,必须隐式转换为Object^。为遵从CLS,在此把函数标为static。(非静态操作符函数不符合CLS。)

  除了句柄符号之外,这个函数与用传统C++编写起来非常类似,然而,最大的不同之处恰恰在于此操作符的使用,如果有这样一种情况,(p == q),p与q都是Point^类型,问题在于,代码的阅读者可能会认为此处是在比较两个句柄,但实际上,是在比较这些句柄引用的Point。为明确地比较句柄,需要这样编写代码:

if (static_cast<Object^>(p1) == static_cast<Object^>(p2))

  虽然也可为Point类提供 == 操作符函数,但仍必须提供Equals,否则,其他人对一个Point调用Equals时,就会转到System::Object中的相应函数,而其比较的是引用相等性,而不是值相等性。也就是说,如果指定Object的实例与当前实例为同一实例,它返回true,否则,返回false。

  CLI数组

  因为标准C++中也存在数组,它们与C语言中的数组也非常类似,也具有同样的利弊关系,即,它们都在编译时分配空间,有固定大小,且没有强制检查数组边界。多维数组并不真正存在,实际上,它们都是数组的数组,或数组的数组的数组等等,在此,我们把这类数组称为"本地数组"。

  而在CLI中,数组则为对象,并分配在垃圾回收堆上,它们的大小在编译时可以为未知状态,且在运行时会自动进行数组边界检查,还支持真正的多维数组。同样地,就需要新的语法来表示这类CLI数组。

  例3:

int main()
{
 /*1*/ array<int>^ numbers = gcnew array<int>(5) {10, 20, 30, 40};
 Display1DArray("numbers", numbers);
 /*2*/ array<Point^>^ points = gcnew array<Point^> {gcnew Point(3,4), gcnew Point(5,7)};
 Display1DArray("points", points);

 /*3*/ numbers = gcnew array<int>{55, 66, 77};
 Display1DArray("numbers", numbers);
 /*4*/ numbers = gcnew array<int>{};
 Display1DArray("numbers", numbers);

 /*5a*/ points[0]->Move(2,5);
 /*5b*/ points[1] = gcnew Point(8,1);
 /*5c*/ Console::WriteLine("points[0] is {0}", points[0]);
}

  请看例3,与引用类的实例相同,CLI数组的对象本质上没有名称,且它们是通过指向它们的句柄来访问的,正如标号1、2、3、4中所见,一个CLI数组类型是用类似于模板的符号进行编写的,如array<int>和arrar<Point^>。(在C++/CLI早期的版本中,必须使用using namespace cli::language,编译器才能理解这个符号,现在已不再需要了。)请仔细留意标号1与2,定义了指向数组的句柄,而不是数组,在标号2中,数组类型为"指向Point的句柄数组",而不是"Point数组"。

  默认以自动方式存储的句柄,值为nullptr,然而,在上面的数组定义中,用gcnew分配了一块内存来初始化句柄,在gcnew后紧跟着数组类型,其后可选的圆括号中指明了元素的个数,再往后可选的花括号中的列明了初始化列表。如果省略初始化列表,元素则取它们的默认值;如果省略元素个数,那么分配的个数则为初始化列表中表达式的个数;如果指定的个数大于列表中的个数,那余下的元素则取它们的默认值。(例如,numbers[4]的值为零。)元素的个数与初始化列表不能同时省略,且它们也不一定要为常量;元素个数可为零,初始化列表也能为空,两者都指明了一个零元素的数组,但这与完全没有数组却有很大的区别。

  在标号2中,省略了元素个数,如果它指定了为3,那第三个元素将会被初始化为nullptr,而不是由默认构造函数生成的指向Point的句柄,这与想像的有点不同。

  在标号3中,把int数组句柄numbers设为了一个新值--包含了3个int的数组,这将导致新数组比旧数组中的元素减少一个,如果减少的是已分配空间中唯一的元素,那它最终将会被垃圾回收器回收。因此,尽管数组可以有一个固定大小,但一个一维或多维数组的句柄,能被重新设为类型与维数不变情况下的任意数组,而不用在乎元素的个数(其由系统维护)。

  正如标号5a、5b与5c中所示,可用下标来访问一维CLI数组。

  函数Display1DArray将显示它第一个参数的文本,第二个参数所指定的数组中元素的个数,及这些元素的具体值(如果有的话),以下是输出:

numbers 5: 10 20 30 40 0
points 2: (3,4) (5,7)
numbers 3: 55 66 77
numbers 0:
points[0] is (2,5)

  例4是Display1DArray的源代码。(眼下,暂时把关键字generic当作template。)

  例4:

generic<typename T>
void Display1DArray(String^ text, array<T>^ ary)
{
 /*6*/ if (ary == nullptr)
 {
  Console::WriteLine("nullptr passed");
  return;
 }
 /*7*/ Console::Write("{0} {1}:", text, ary->Length);
 /*8*/ for each (T element in ary)
 {
  Console::Write(" {0}", element);
 }
 /*9*/ Console::WriteLine();
}

  很明显,参数ary是一个类型T的CLI数组句柄,但是,ary不但可为一个数组的句柄,也能为一个nullptr值的句柄,所以在标号6中,对它进行了相应的检查。

  在标号7中,显示了传入进来的文本及数组中元素的个数,而后者是用只读属性Length获取的,这是所有数组都有的属性。(所有的CLI数组都隐式从类System::Array继承,而类System::Array则具有Length属性。)

  接下来,在标号8中循环遍历了此数组,并在同一行中显示出每个元素,并以标号9中的新行结束。此处,没有使用一个从零到ary->Length - 1的整数索引,而是使用了新的循环语句:for each(它们两者合成为一个关键字),用它来枚举集合中的元素。在此,不必深究for each的工作原理,只需了解,一个CLI数组也是一个CLI的集合,当然也可使用这种语法来进行遍历了。虽然使用此方法不能访问到每个元素的下标索引,但要知道,并不是所有集合都是线性的(如二叉树),在这种情况下,索引毫无意义。

  当使用下标来访问CLI数组时,并不是使用数组的 [] 操作符,而是使用了定义在System::Array中,称为Item的索引属性。虽然一个标量属性只能为一个单一的值,但一个索引属性却能有不同的值,且每个值都能用下标来访问。

  为使Display1DArray对数组类型不敏感,一般会想到template,然而,template是一个编译时的操作,对程序中使用的每种数组类型,都会产生一个相应的副本。在版本V2中,CLI添加了对generic的支持,generic是一个依赖于上下文的标识符,它的功能类似于模板,但它在运行时只会有单一的一份副本。(与大家想的一样,也有generic类型。)
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章