operator算子&隐式类型转换

对于 operator 运算符,除了常用的 operator overloading(操作符重载)之外,还有 operator casting(操作隐式转换)的用法。



1. 操作符重载

c++可以通过operator实现重载操作符,格式如下:return_type operator operand (parameter list),比如重载+,比如下面这个例子:

template<typename T> class A
{
public:
     const T operator+(const T& rhs)
     {
         return this->m_ + rhs;
     }
private:
     T m_;
};

又比如STL中的函数对象/STL仿函数重载operator(),如下面例子:

template<typename T> struct A
{
   T operator()(const T& lhs, const T& rhs){ 
   		return lhs-rhs; 
   }
};

其他的操作符重载不再赘述。



2. 隐式类型转换

隐式类型转换又可分为构造函数的隐式类型转换(附含对explicit关键字的作用和介绍)和operator算子的隐式类型转换。


2.1 构造函数的隐式类型转换

《C++ Primer》中有这样一句话:可以用单个实参来调用的构造函数定义了从形参类型到该类类型的一个转换。这句话用下面一段代码表示为:

class A
{
    A(B b);//单个实参的构造函数
    func(A a);
} 
.....
A a;
B b;
a.func(b);
//func函数本应该接受A类型的实参,但是由于特殊构造函数的存在,
//所以B类型的参数b借助这个特殊的构造函数转化为A类型对象,完成转换。
//所以这条语句是正确的

从上述代码段可以看出来,单个实参调用的构造函数定义了类类型到其他类型的转换,并且这种转换是隐式发生的,这里有几个关键字:单个实参构造函数隐式转换

隐式转换的过程发生了什么?对象a并没有参数类型为B的成员函数,但是它有一个单一B类形参的构造函数,所以编译时是不会报错的。对于a.func(b)这条语句,编译器会用这个特殊的构造函数,生成一个临时对象,然后以临时对象调用正规的 func(A a)函数,func(A a)函数结束,临时对象被注销。
这种转换到底好不好?因类而异,因使用的语境而异!你总有需要它的时候,也有不许要它的时候,语言默认是有这项功能的。但是,也可以通过关键字explicit阻止这种我们看不到的“优化”!explicit关键字只能用在构造函数身上,并且只需在函数的声明时标注即可,在类函数的定义时不需标注该关键字。

上例中如果阻止借助构造函数A(B b)发生隐式类型转换,可对该构造函数的声明做如下处理:

explicit A (B b);

此时语句a.func(b)就要出错了,但是我们可以显式地使用构造函数,以上例为例,可以使用语句:

a.func(A(b));

完成相同的功能,此时不涉及隐式转换。A(b)生成临时A类型对象,并且传递给函数func调用,一切循规蹈矩,没有任何隐式的、程序员看不见的步骤。显示的构造函数中止了隐式地使用构造函数,任何构造函数都可以显式地创建临时对象,这是它的权利,被explicit修饰的构造函数也不例外。

对于隐式类型转换:通常,除非有明显理由要定义隐式转换(如资源管理类型需要向APIs提供底层资源访问,见条款16:在资源管理类中提供对原始资源的访问中为客户提供方便),否则,单形参构造函数应该为explicit。将构造函数设置为explicit可以避免错误,并且当转换有用时,用户可以显式地构造对象


2.2 operator 算子的隐式类型转换

C++可以通过operator实现重载隐式转换,格式如下: operator cast_to_type_T (),其中T是一个类型,比如下面这个例子:

class A {
public:
   operator   B*() { return this->b_; }
   operator const   B*() { return this->b_; }
   operator   B&() { return *this->b_; }
private:
   B* b_;
};

A a;
//当if(a),编译时,它转换成if(a.operator B*()),
//其实也就是相当于 if(a.b_)

还有一例是《Effective C++》中条款15:在资源管理类中提供对原始资源的访问p71:

class Font {
public:
	...
	operator FontHandle() const
	{ return f; }
	...
};

例子之后,是操作隐式转换的语法:

class A {  
public:  
    ......  
    operator int(){return num;};  
    // 在需要情况下, A对象可以转成int类型对象。  
    ......  
    int num;  
 } c;  

int main() {
	//这条声明使得以下语句成立:
	cout<<c+12<<endl; //其中c是A类型的对象  
	//这里对于编译器相当于
	//cout << c.operator int() + 12 << endl;
	//也相当于
	//cout << c.num + 12 << endl;
}

2.3 两种隐式类型转换的比较

  构造函数的隐式类型转换部分,使用一个其他的类型构造当前类的临时对象,这种转换必须有构造函数的支持;operator 算子的隐式类型转换,使用当前对象去生成另一个类型的对象(正好与构造函数型相反),这种转换必须有 operator 算子的支持。

  隐式的类型转换有利有弊,类的设计者就起决定性作用了,如果你不想让构造函数发生隐式的类型转换,请在构造函数前加 explicit 关键字;同时,operator 算子声明的隐式类型转换也可以通过一些相应的返回值函数替代(即显示类型转换,安全性更高),对客户而言使用起来屏蔽性更好更方便。



3. 参考资料

  1. 《C++ Primer》
  2. 《Effective C++》
  3. CSDN:C++ 中operator用法:隐式类型转换
  4. 博客园:C++ 中operator用法:隐式类型转换
  5. 隐式类类型转换
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值