拷贝构造函数 在下面的例子中,p6由给定的坐标构造而成,而p7则初始化为p6的一个副本,这就需要Point有一个拷贝构造函数;然而,在默认情况下,编译器不会为这些引用类产生一个拷贝构造函数。那么,在这种情况下,就必须自己编写一个。
以下,是Point的拷贝构造函数:
Point(Point% p) { X = p.X; Y = p.Y; } |
而对一个本地类N的拷贝构造函数,一般声明成如下形式:
但是,对引用类来说,因为%取代了&,所以在CLI的世界中,const显得有点格格不入。
赋值操作符 以下表达式:
就需要一个赋值操作符,但再次提醒,这不是自动提供的。以下就是一个自定义的操作符例子:
Point% operator=(Point% p) { X = p.X; Y = p.Y; return *this; } |
之所以没有提供默认的拷贝构造函数或赋值操作符,是因为所有的引用类(除了System::Object),都有一个基类:System::Object,而这个类并没有提供一个拷贝构造函数或赋值操作符。基本上,这两者默认都会调用它们基类中相应的实现版本,但基类中却一个对应的定义也没有。
相等性操作符
通过为Point定义一个拷贝构造函数和一个赋值操作符,就可以处理那些数值类型的实例了,你可以初始化它们、把它们传给函数、或把它们从函数中返回;但实际上,可能还再需要一个操作符--相等性比较操作符,它能像如下定义:
static bool operator==(Point% p1, Point% p2) { if (p1.GetType() == p2.GetType()) { return (p1.X == p2.X) && (p1.Y == p2.Y); } return false; } |
由于一个跟踪引用不可能为数值nullptr,所以就不必对此值进行检查了,又由于p1与p2是两个Point的别名,所以可使用点操作符调用GetType和属性X与Y的get程序。
能同时满足两方面需求吗?
以前说过,对一个引用类而言,相等性的判别是通过一个Equals函数而不是重载 == 操作符来实现的,并且重载了一个接受句柄的 == 操作符,指出了使用上的问题。那让我们再来回顾一下这个话题。
当在C++/CLI中设计并实现一个引用类时,就要想到"这个类的使用者,会使用C++/CLI语言进行编程,还是会使用如C#、J#、VB.NET之类的其他语言呢,或者两者都使用呢?"
C++程序员习惯于把类实例当作数值来对待,所以,他们期待类中有一个拷贝构造函数及一个赋值操作符,且对某些类来说,还会期待实现相等或不相等操作符;另一方面,C#、J#、VB.NET程序员只能通过句柄来操纵类实例,所以他们只想要克隆或Equals函数,至于拷贝构造函数与赋值操作符,他们无须知道,也无须关心。
即便C++程序员更倾向于使用 == 操作符,但一个带有Equals函数的引用类可被任意语言所调用,所以在设计引用类时应尽量实现此函数,不过话说回来,如果对一个不包含Equals函数的类实例调用此函数,将会产生无法预料的后果。
如果在一个引用类中,提供了可接受两个跟踪引用的 == 操作符函数,一般上也可满足C++/CLI程序员的需要。虽然也能提供一个接受两个句柄的 == 操作符函数,但似乎不可能被这两组程序员使用。
简而言之,既可为C++/CLI程序员,也可为其他.NET语言程序员、或同时为两者实现一个引用类,那么,是不是可把它们简单地分为C++/CLI与"其他语言"两个阵营呢,但事情似乎总不是这么简单的,举例来说,虽然System::String是一个引用类,它提供了可接受两个句柄的 == 操作符与 != 操作符函数,但是,比较的是字符串的值,而不是它们的句柄。一般来说,在引用类中使用值这个说法,是有点让人感觉怪怪的,但对一个string类来说,却又是合情合理的。
在此非常清楚的一点是,万能的方法是不存在的。为对引用类的使用者,提供最适当的接口,就必须在基于他们所使用语言的基础上,多考虑一下他们的期望。但无论如何,C++/CLI程序员想要使用其他语言创建的引用类,就不得不要适应没有拷贝构造函数与赋值操作符这些情况。

发表评论