【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++规则可知,这样的转换程序是禁止的。

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

原文地址:https://www.cnblogs.com/next-IT-direction/p/3598775.html