1)C++允许内置数据类型之间进行隐式转换,比如char转int,int转double,对于内置数据类型的转换有详细的规则,但不管怎么样,这些都是语言提供的,相对安全,而且我们无法更改

对于自定义类的类型,其隐式类型可以通过带单一自变量的构造函数和隐式类型转换操作符来实现

2)单一自变量构造函数:指能够以单一自变量成功调用的构造函数,该构造函数可能只有一个参数,也可能有多个参数,并且除了第一个参数外其他的都有默认值

class Name
{
public:
Name(const string &s)//可以把string转化为Name
{ }
};

3)隐式类型操作转换符

样例1:

#include<bits/stdc++.h>
using namespace std; class Retional
{
private:
int numerator;
int denominator;
public:
Retional(int x,int y)
{
numerator=x;
denominator=y;
}
operator double() const//将 Retional 转换为 double
{
return numerator * 1.0 / denominator;
}
}; int main()
{
Retional r(1,2); cout<<r<<endl;//0.5 double x=0.5*r;//隐式类型转换函数在这种情况下会被调用,很隐秘,将Retional类型转化为了double类型 cout<<x<<endl;//0.25
}

隐式类型转换函数存在的问题:其根本问题就在于,在你从未打算也未预期的情况下,此类函数可能会被调用,而且结果也可能是不正确的,不直观的程序行为,很难调试

解决方案:提供一个功能对等的显式函数来取代隐式类型转换函数,通过显示的调用该函数来完成类型转换

#include<bits/stdc++.h>
using namespace std; class Retional
{
private:
int numerator;
int denominator;
public:
Retional(int x,int y)
{
numerator=x;
denominator=y;
}
double toDouble() const //显式 类型转换函数
{
return numerator*1.0/denominator;
}
}; int main()
{
Retional r(1,2); //cout<<r<<endl; //error 没有重载<< cout<<r.toDouble()<<endl;//0.5 显式的调用转换函数比隐式的类型转换函数更加可靠 double x=0.5*r.toDouble(); cout<<x<<endl;//0.25
}

样例2:

#include<bits/stdc++.h>
using namespace std; template<class T>
class Array
{
Array(int lowbound,int highbound)
{ }
Array(int size)
{ }
T& operator [](int index)
{ }
}; bool operator==(const Array<int> &lhs,const Array<int> &rhs)
{ } int main()
{
Array<int> a(10);
Array<int> b(10); for(int i=0;i<10;i++)
{
//应该是a[i]==b[i],但是此时编译器没有报错!!它会通过Array(int size)将b[i]隐式的转换成Array,
//然后每次迭代都用a的内容和这个数组比较,
//这不仅没有实现功能,并且很没有效率,因为必须产生和销毁这个临时变量
if(a==b[i])
{
//do something
}else
{
//do something
}
}
return 0;
}

应该是a[i]==b[i],但是此时编译器没有报错!!它会通过Array(int size)将b[i]隐式的转换成Array,然后每次迭代都用a的内容和这个数组比较,这不仅没有实现功能,并且很没有效率,因为必须产生和销毁这个临时变量

解决方案1:采用explicit关键字,禁止编译器对该关键字修饰的函数进行隐式类型转换

#include<bits/stdc++.h>
using namespace std; template<class T>
class Array
{
public: Array(int lowbound,int highbound)
{ }
explicit Array(int size)
{ }
T& operator [](int index)
{ }
}; bool operator==(const Array<int> &lhs,const Array<int> &rhs)
{ } int main()
{
Array<int> a(10);
Array<int> b(10); for(int i=0; i<10; i++)
{
//if(a==b[i]){} //error 加了explicit无法隐式转换 if(a==Array<int>(b[i]))//可行,调用显示构造函数
{ }
if(a==static_cast<Array<int> >(b[i]))//可行,调用C++类型转换函数
{ }
if(a==(Array<int>)(b[i]))//可行,C的旧式转型
{ }
}
return 0;
}

解决方案2:采用内部代理类

C++中存在这样一条规则:没有任何一个转换程序可以内含一个以上的“用户定制转换行为(即单自变量的构造函数和隐式类型转换符)”,也就是说,必要的时候编译器可以先进行内置类型之间的转换再调用带单自变量的构造函数或者先调用隐式类型转换符再进行内置类型的转换,但不可能连续进行两次用户定制的转换行为

#include<bits/stdc++.h>
using namespace std; template<class T>
class Array
{
public:
class ArraySize //内部代理类
{
private:
int thesize;
public:
ArraySize(int numElements):thesize(numElements){}
int size() const
{
return thesize;
}
};
Array(int lowbound,int highbound)
{ }
explicit Array(ArraySize size)//使用内部代理类进行参数声明
{ }
T& operator [](int index)
{ }
}; bool operator==(const Array<int> &lhs,const Array<int> &rhs)
{ } int main()
{
Array<int> a(10);
Array<int> b(10); for(int i=0; i<10; i++)
{
if(a==b[i])//因为内部代理类的存在,所以编译无法通过
{ }
}
return 0;
}

通过使用内部代理类,不但可以以一个整数作为构造函数的自变量来指定数组的大小,又能阻止一个整数被隐式的类型转换未一个临时的Array对象!

很值得学习的一种模式

避免隐式类型转换函数被调用的三种方式:

1)提供一个和隐式类型转换函数功能相同的显式函数

2)使用explicit修饰隐式类型转换函数,禁止该函数被调用

3)使用内部代理类


总结:允许编译器执行隐式类型转换,害处将多过好处,所以不要提供这种隐式的类型转换,除非你真的真的很需要!

【More Effective C++ 条款5】对定制的“类型转换函数”保持警觉的更多相关文章

  1. 【M5】对定制的“类型转换函数”保持警觉

    1.隐式类型转换有两种情况:单个形参构造方法和隐式类型转换操作符.注意:隐式类型转换不是把A类型的对象a,转化为B类型的对象b,而是使用a对象构造出一个b对象,a对象并没有变化. 2.单个形参构造方法 ...

  2. effective c++ 条款5 c++ 默默实现的函数

    当写一个空类c++ 会为我们自动提供四个函数 1 默认构造函数 2 默认析构函数 3 拷贝构造函数 4 默认赋值运算符

  3. [More Effective C++]条款22有关返回值优化的验证结果

    (这里的验证结果是针对返回值优化的,其实和条款22本身所说的,考虑以操作符复合形式(op=)取代其独身形式(op),关系不大.书生注) 在[More Effective C++]条款22的最后,在返回 ...

  4. More Effective C++ 条款0,1

    More Effective C++ 条款0,1 条款0 关于编译器 不同的编译器支持C++的特性能力不同.有些编译器不支持bool类型,此时可用 enum bool{false, true};枚举类 ...

  5. 《More Effective C++》 条款5 谨慎定义类型转换函数

    ---恢复内容开始--- C++编译器能够在两种数据类型之间进行隐式转换(implicit conversions),它继承了C语言的转换方法,例如允许把char隐式转换为int和从short隐式转换 ...

  6. 读书笔记 effective c++ Item 46 如果想进行类型转换,在模板内部定义非成员函数

    1. 问题的引入——将operator*模板化 Item 24中解释了为什么对于所有参数的隐式类型转换,只有非成员函数是合格的,并且使用了一个为Rational 类创建的operator*函数作为实例 ...

  7. Effective C++ -----条款18:让接口容易被正确使用,不易被误用

    好的接口很容易被正确使用,不容易被误用.你应该在你IDE所有接口中努力达成这些性质. “促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容. “阻止误用"的办法包括建立新类型.限 ...

  8. Effective C++ 条款08:别让异常逃离析构函数

    1.别让异常逃离析构函数的原因 <Effective C++>第三版中条款08建议不要在析构函数中抛出异常,原因是C++异常机制不能同时处理两个或两个以上的异常.多个异常同时存在的情况下, ...

  9. Effective C++ -----条款48:认识template元编程

    Template metaprogramming(TMP,模板元编程)可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的执行效率. TMP可被用来生成“基于政策选择组合”(based on ...

随机推荐

  1. vue.js环境在window和linux安装

    一.windows环境下安装vue 1.node.js安装:在node.js的官网上下载node的安装包 https://nodejs.org/en/download/ 安装完毕之后,在命令行下验证是 ...

  2. Scrapy-Splash简介及验证码的处理(一)

    目录 一:Splash简介与准备 二:验证码的识别(1)   在之前的博客中,我们学习了selenium的用法,它是一个动态抓取页面的方法,但是,动态抓取页面还有其他的方法,这里介绍Splash方法, ...

  3. python 父类方法中使用不同的子类中的不同类对象

    # coding:utf-8 class Animal(object): def __init__(self): self._name = None self._f = None def eat(se ...

  4. 洛谷 p1541乌龟棋

    洛谷 p1541乌龟棋 题目背景 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 题目描述 乌龟棋的棋盘是一行NN个格子,每个格子上一个分数(非负整数).棋盘第1格是唯一的起点,第NN格是终点,游戏 ...

  5. Ext.create使用(下)

    本文介绍第三种使用方法: //通过类的引用实例化一个类 var w1 = Ext.create(Ext.window.Window, {//类的引用 title: '窗体', html:'<fo ...

  6. i春秋四周年中奖名单出炉丨确认过眼神,你是中奖人

    i春秋四周年任性狂欢倒计时最后2天! 优享会员.精品课程.CTF经典赛题实战班.Web安全线上提高班.渗透测试工程师线下就业班.CISAW-Web安全认证......全部史上最低折扣,还有8888元现 ...

  7. wpf DATAgrid模板中button 命令绑定以及命令参数绑定

    场景:视频上传功能,上传列表使用DataGrid控件,视频有不同的状态对应不同的操作,DataGrid中最后一列为操作列,里面是Button控件.希望点击Button后执行对应的操作,但是设置Butt ...

  8. pwrite

    写入起始地址的偏移量,写入地址=文件开始+offset 注意:执行后,文件偏移指针不变 ssize_t pread(int fd, void *buf, size_t count, off_t off ...

  9. POJ 1321 棋盘问题 题解

    棋盘问题 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 70224 Accepted: 33254 Description 在一 ...

  10. 关于k8s集群证书1年过期后,使用kubadm重新生成证书及kubeconfig配置文件的变化

    这个证书很重要,不用说. 但手工生成证书,确实工作量大且容易出错. 推荐的方式,是保留/etc/kubernetes/pki目录下的ca.crt,ca.key,sa.crt,sa.key. 这四个文件 ...