当前位置:天才代写 > tutorial > C语言/C++ 教程 > 理会C++/CLI之头文件、内联函数与数组

理会C++/CLI之头文件、内联函数与数组

2017-11-07 08:00 星期二 所属: C语言/C++ 教程 浏览:830

副标题#e#

头文件与函数声明

在传统C++的设计与实现中,你可对需建模的每种范例举办界说,并把界说放在各自的头文件中;而头文件中,一般会包括范例名、成员名、及相关小型成员函数的内联界说。

与各个单独编译的源文件是通过甚文件来共享信息差异,在C++/CLI中,这些信息是通过措施集来共享的。就拿常举例的Point类来说,它单独编译,并生成了一个名为"Point.dll"的措施集。任何需要某种范例界说的应用措施,都必需编译和链接带有此范例的措施集,这同时也要求此DLL形式的措施会合有完整的范例界说;同样,在范例中所有声明的函数也必需被界说,不然,链接器将会陈诉错误。

举例来说,你可以在Point类中声明成员函数GetHashCode,并在类外界说它,但必需在同一源文件中(见例1)。可是,若把此成员函数的界说放在一个单独的源文件中却不可,即便源文件是作为同一措施集的输入、与Point.cpp同时编译也不可,因为编译这样一个文件需要会见措施集Point.dll,而这正好是此编译进程要生成的措施集。(此处假定在函数界说时未利用inline,这将在后头接头。)

例1:

public ref class Point
{
...
virtual int GetHashCode() override;
};
int Point::GetHashCode() override
{
return X ^ (Y << 1);
}

在编译及链接任何措施集时,都隐含不利用头文件,且措施集所依赖的所有其他措施集都必需是已编译及链接过的。

内联函数

在Point中,每个成员函数的界说都有意写成了inline(内联),除了增加界说的机动性外,还可把代码保持在同一源文件中,使成员函数不能在范例界说自己之外的另一文件中被界说。


#p#副标题#e#

在标号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]);
}

#p#副标题#e#

请看例3,与引用类的实例沟通,CLI数组的工具本质上没有名称,且它们是通过指向它们的句柄来会见的,正如标号1、2、3、4中所见,一个CLI数组范例是用雷同于模板的标记举办编写的,如array<int>和arrar<Point^>。(在C++/CLI早期的版本中,必需利用using namespace cli::language,编译器才气领略这个标记,此刻已不再需要了。)请仔细寄望标号1与2,界说了指向数组的句柄,而不是数组,在标号2中,数组范例为"指向Point的句柄数组",而不是"Point数组"。

#p#分页标题#e#

默认以自动方法存储的句柄,值为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的荟萃,虽然也可利用这种语法来举办遍历了。固然利用此要领不能会见到每个元素的下标索引,但要知道,并不是所有荟萃都是线性的(如二叉树),在这种环境下,索引毫无意义。

#p#副标题#e#

当利用下标来会见CLI数组时,并不是利用数组的 [] 操纵符,而是利用了界说在System::Array中,称为Item的索引属性。固然一个标量属性只能为一个单一的值,但一个索引属性却能有差异的值,且每个值都能用下标来会见。

为使Display1DArray对数组范例不敏感,一般会想到template,然而,template是一个编译时的操纵,对措施中利用的每种数组范例,城市发生一个相应的副本。在版本V2中,CLI添加了对generic的支持,generic是一个依赖于上下文的标识符,它的成果雷同于模板,但它在运行时只会有单一的一份副本。(与各人想的一样,也有generic范例。)

C++/CLI支持真正的多维数组

CLI数组的第二个参数为可选,其代表了一个数组的序(也等于数组的维数),默认为1。好比在前一个例子中,array<int>也能写成array<int,1>。(与模板的非范例参数相似,其必需为一个编译时的常量。)

例5:

int main()
{
  /*1*/ array<String^, 2>^ names = gcnew array<String^, 2> (2,3) {
  {"John", "Robert", "Peter"},
  {"Mary", "Alice"}
};
/*2*/ Console::WriteLine("names has {0} elements", names->Length);
/*3*/ Console::WriteLine("names has {0} dimensions", names->Rank);
/*4*/ Console::WriteLine("names[0,0] is {0}", names[0,0]);
/*5*/ names = gcnew array<String^, 2> (5,7);
/*6*/ Console::WriteLine("names has {0} elements", names->Length);
}

#p#分页标题#e#

请看例5,在标号1中,界说了一个指向String句柄的两维数组的句柄,但并未指明行数或列数,接下来,分派了一个2×3数组的内存空间,并以5个字符串及一个nullptr初始化了这6个数组元素,以下是输出:

 

    关键字:

天才代写-代写联系方式