科技行者

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

知识库

知识库 安全导航

至顶网软件频道C++/CLI解析之基于堆栈的对象与跟踪引用2

C++/CLI解析之基于堆栈的对象与跟踪引用2

  • 扫一扫
    分享文章到微信

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

通过为Point定义一个拷贝构造函数和一个赋值操作符,就可以处理那些数值类型的实例了,你可以初始化它们、把它们传给函数、或把它们从函数中返回;但实际上,可能还再需要一个操作符--相等性比较操作符,它能像如下定义:

作者:中国IT实验室 来源:中国IT实验室 2007年10月2日

关键字:

  • 评论
  • 分享微博
  • 分享邮件

相等性操作符

  通过为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程序员想要使用其他语言创建的引用类,就不得不要适应没有拷贝构造函数与赋值操作符这些情况。

其他话题

  以下的话题非常简短、但却十分有帮助:

  1、前面也提到,const不是很适合CLI,且在引用类中,C++/CLI也不允许用const(或volatile)来限定成员函数;但个关键字可用在某些类中实例构造函数或成员函数上。那么在此情况下,它的类型到底是什么呢?对本地类型N来说,它是N* const;然而,对一个引用类R来说,它只是一个R^。虽然句柄不是const限定的,但它的值却不能被修改。

  2、Point::ToString的实现使用了如下形式:

return String::Concat("(", X, ",", Y, ")");

  另外也可以像下面这样写:

return String::Format("({0},{1})", X, Y);

  正如它的名字,Format函数允许对文本进行格式化(如前导的空格或零、或者一些分隔符等等),而不是简单地对字符串进行连接。

  3、如果想要知道编译器是否支持C++/CLI扩展,可测试__cplusplus_cli宏是否已经预定义。如果已定义,它的值将为200406L。

  4、CLI库包含了一个称为System::Decimal的类型,其至少可表示28位数字的值,这个类型是专为那些需要在没有四舍五入情况下进行大数额金融计算而准备的。与浮点类型不同,Decimal的小数部分能表示得更加精确,通常,当某数以浮点类型表示时,其经常会有一个无穷的小数,但却更容易导致舍入上的错误;而Decimal有一个称为"scale(数值范围)"的属性,其代表了十进制下所需的位数。举例来说,2.340的scale为3,其结尾的0非常重要,当两个十进制数相加或相减时,结果的数值范围是两者数值范围中较大的一者,如:1.0 + 2.000为3.000,而5.0-2.00为 3.00;当两个十进制数相乘时,结果的数值范围是两者数值范围之和,如:1.0*2.000为2.0000;当两个十进制相除时,结果的数值范围是除数的数值范围比被除数多出的值,如:4.00000/2.000为2.00。然而,数值范围不可能小于为表示正确的值所需的范围,例如,3.000/2.000、3.00/2.000、3.0/2.000和3/2都是1.5,下面是Decimal的使用范例:

Decimal x = Decimal::Parse("23.00");
Decimal y = Decimal::Parse("2.000");
Decimal result = x * y + Decimal::Parse("2.5");
Console::WriteLine(result);

  输出为48.50000。请注意,C++/CLI没有字面意义上的Decimal类型,所以需要使用Parse函数。

  5、如果有这样一种情况,一个类使用了C++/CLI之外的其他语言编写,且有一个名字为C++/CLI关键字的public成员,那就可通过__identifier(x)这种形式的内部函数来访问它,此处的x可以是一个标识符、一个关键字、或一个字面上的字符串,例如,为调用一个类X中名为delete、且没有参数的静态函数,就可以像X::__identifier(delete)()这样使用。

  6、一个literal域是一个定义在类中的命名编译期常量,同样,它也必须有接受一个常量值的初始化程序。尽管一个literal域使用上像一个静态数据成员,但它并不能声明为static,且编译器会把每个literal域都替换为域值。一个literal域能有任意标量类型,但是,能用作初始化句柄的唯一常量数值,只能为字面上的字符串和nullptr。

literal double PI = 3.1415926;
literal int MinValue = -10, MaxValue = 10;
literal int Range = MaxValue - MinValue + 1;
enum Direction {North, South, East, West};
literal Direction Home = North;
literal System::String^ Title = "Annual Report";

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

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

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