zoukankan      html  css  js  c++  java
  • 【More Effective C++】Item 5

    Item 5: 对定制的“类型转换函数”保持警觉

      C++从C语言那继承了其隐式转换,可以实现不同类型之间的转换,且C++的隐式转换还存在更令人害怕的转型,有可能出现数据丢失。对于这些,你无能为力,因为这都是语言自身提供的。然而当你的自定义类型登场时,你可以通过提供某些函数,来作为编译器的转型之用。但是为什么最好不要提供任何类型转换函数呢?

      原因在于:在你为打算也为预期的情况下,此类函数可能会被调用,而其结果可能不正确、不直观,很难调试。  

      在C++中,编译器会通过两种方式来实现类型的隐式转换:

      1、使用隐式类型转换符,即关键词operator之后加上一个类型名称,如operator double(),注意:你不能为此函数指定返回值类型

      2、通过单变量的constructor,包括只有一个参数的constructor,也包括多个参数,但除了第一个参数外其他的参数都是有缺省值的constructor,如Constructor_Name(Type name)和Constructor_Name(Type1 name1,Type2 name2=Default_Value)。

      下面我们来分别介绍这两种方式别调用的调节以及应对措施:

      第一种:使用operator重载函数

      当为自定义类提供operator double()重载函数时,该重载函数会在以下情况被调用:ClassType temp(1,2);  double d=0.5*temp;

      大概转换过程是:对象temp调用operator double()转换为double temp=1/2;然后与0.5相乘,最后把结果赋值给变量d。

      这种情况比较好解决:我们只要以功能对等的另一个函数取代类型转换操作符,如将operator double() 转换为 double asDouble()即可。

      这样,当需要转型时,需要显示的调用该member function才能实现。

     第二种:通过单变量constructor

      通过单自变量constructor完成的隐式转换较难消除。如下例所示:

      template <class T>

      class Array{

      public:

        Array(int size);

        ...

        T& operator[] (int index);

      };

      Array<int> a(10);

      Array<int> b(10);

      for(int i=0; i<10; i++) if(a == b[i]){do something ...} else {do other things...}

      因为代码中把a[i]写成了a,编译器原本应该给出错误或者警告提示的,但是C++编译器很聪明,它会想尽办法找到一个合适的函数以使程序顺利执行,因此它发现只要将b[i]通过constructor就可以转换为Array<int>类型的对象,于是它就放手去做了。

      于是问题就出现了,循环的每次比较都发生在a的内容和一个大小为b[i]的临时数组做比较,结果可想而知。且这样做因为每次都要构造和析构一个临时对象,所以十分没效率。

      那该怎么解决呢?

      1、如果你的编译器支持explicit关键词,就容易解决,只要将constructor做如下声明即可;

      explicit Constructor_Name(Type var);这样编译器就不会因隐式转换而调用它了,不过显式类型转换仍是允许的哦。

      2、如果你的编译器不支持explicit关键词,那么只能走弯路,利用C++的规则中的一条:没有任何一个转换程序可以内含一个以上的“用户定制转换行为”

      我们可以将Array类的constructor进行变换,产生一个新类ArraySize类,该类只用于表明数组的大小。即

      class Array {

      public:

        class ArraySize {

        public:

          ArraySize(int num):theSize(num);

          ...

        };

      Array(ArraySize size);

      }

      如此一来,当我们定义Array类的对象时:Array<int> a(10);编译器会发现可以利用ArraySize类的constructor实现int->ArraySize object的转换,它毫不犹豫地做了,事实也证明这样做是对的,Array类的constructor的确需要一个ArraySize类的object。对于这个情况是我们需要的隐式转换,但是对于其他情况呢?

      我们来看另一种情况,同上例,if(a == b[i]),我们将a[i]写成了a,此时会不会发生隐式转换呢?答案是否定的。

      因为对于上面的情况,如果可以成功执行,需要两个转换:1)通过ArraySize类的constructor将int b[i]转换为ArraySize的对象;2)再通过Array类的constructor将ArraySize类的对象转换为Array<int>类型的对象。由C++规则可知,这样的转换程序是禁止的。

      允许编译器执行隐式类型转换,害处将多过好处,因此不要提供转换函数,除非你确定你真的需要他们。

  • 相关阅读:
    Linux——shell简单学习(一)
    Linux——进程管理简单学习笔记(二)
    Linux——进程管理学习简单笔记
    Linux——用户管理简单学习笔记(四)
    PHP计算程序运行时间的类
    php几个常用的概率算法(抽奖、广告首选)
    限制非安全IP访问
    简单的点击短信发送计时器
    php 以图搜图
    递归获取二维数组后代、删除后代
  • 原文地址:https://www.cnblogs.com/next-IT-direction/p/3598775.html
Copyright © 2011-2022 走看看