Think_in_CPP第十二章 操作符重载(7)

12.11 自动类型转换(Automatic type conversion)

#include <iostream> 
using namespace std; 

class One { 
public: 
   int i; 
   One() { i=1; cout << "One" << i<< endl;} 
}; 
class Two { 
        int i; 
public: 
   Two(const One& a) { i=2;cout << "Two" << i << a.i << endl;} 
}; 
void f(Two) {} 
int main() { 
   One one; 
   f(one); // Wants a Two, has a One 
} 

输出结果

       One1
       Two21

可以看到Two的构造函数被调用,传入的参数是一个One对象。也就是自动类型转换发生了。发现当前类型不是所需要类型的时候,编译器试图寻找可以生成需要类型的方法。在上面的例子中需要Two类型,但是传入的是One,编译器发现Two的有一个只有一个参数构造函数,其参数类型为One于是,类型转换发生了。

要求:
1)构造函数有一个参数,且为被转换对象所属类型
2)构造函数除了被转换对象所属类型作为其中一个参数外,还有其他参数,且都提供了默认值
3)不能建立基于构造函数的用户定义类型到内建类型的自动转换,只能通过操作符重载这一方式

阻止基于构造函数的自动类型转换
方法是在构造函数前面加上关键字:explicit

class One { 
public: 
   One() {} 
}; 
class Two { 
public: 
   explicit Two(const One&) {} 
}; 
void f(Two) {} 
int main() { 
   One one; 
//! f(one); // No auto conversion allowed 
   f(Two(one)); // OK -- user performs conversion 
} 

通过操作符重载来实现类型自动转换(Operator conversion)

class Three { 
  int i; 
public: 
  Three(int ii = 0, int = 0) : i(ii) {} 
}; 
class Four { 
   int x; 
public: 
   Four(int xx) : x(xx) {} 
   operator Three() const { return Three(x); } 
}; 
void g(Three) {} 
int main() { 
   Four four(1); 
   g(four); 
   g(1); // Calls Three(1,0) 
} 

要点:
1)作为类的成员函数
2)形式:operator 目标类名()
3)在使用的时候,传入一个Four对象,但是需要的是Three对象,而Four类又有一个可用于类型转换的操作符重载函数,这个时候转换发生


Reflexivity
通过一个例子来说明成员函数方式和全局函数方式的差别

class Number { 
  int i; 
public: 
   Number(int ii = 0) : i(ii) {} 
   const Number 
   operator+(const Number& n) const { 
     return Number(i + n.i); 
   } 
   friend const Number 
     operator-(const Number&, const Number&); 
}; 
const Number 
   operator-(const Number& n1, 
             const Number& n2) { 
     return Number(n1.i - n2.i); 
} 


int main() { 
   Number a(47), b(11); 
   a + b; // OK 
   a + 1; // 2nd arg converted to 
//! 1 + a; // Wrong! 1st arg not 
   a - b; // OK 
   a - 1; // 2nd arg converted to 
   1 - a; // 1st arg converted to 
} 
成员函数方式要求左值必须是确定的类型,不能通过自动转换方式得到。所以1+a处编译错误。而全局函数方式则没有这个限制1-a处,1就自动转成了需要的类型

有些时候利用类型转换的特性可以带来编码的便利

#include "../require.h" 
#include <cstring> 
#include <cstdlib> 
#include <string> 
using namespace std; 
class Stringc { 
  string s; 
public: 
  Stringc(const string& str = "") : s(str) {} 
  int strcmp(const Stringc& S) const { 
    return ::strcmp(s.c_str(), S.s.c_str()); 
  } 
}; 
 

int main() { 
   Stringc s1("hello"), s2("there"); 
   s1.strcmp(s2); 
}

利用类型转换特性加以改造

#include "../require.h" 
#include <cstring> 
#include <cstdlib> 
#include <string> 
using namespace std; 
class Stringc { 
   string s; 
public: 
   Stringc(const string& str = "") : s(str) {} 
   operator const char*() const { 
     return s.c_str(); 
   } 
}; 

int main() { 
   Stringc s1("hello"), s2("there"); 
   strcmp(s1, s2); // Standard C function 
   strspn(s1, s2); // Any string function! 
} 

自动类型转换的陷阱
自动类型转换要注意一些问题:如一个类型到另外一个类型有1种以上的转换路径;或者是和重载函数一同使用时要转换的目标类型模糊等
class Orange; // Class declaration 
class Apple { 
public: 
   operator Orange() const; // Convert Apple to Orange 
}; 
class Orange { 
public: 
   Orange(Apple); // Convert Apple to Orange 
}; 
void f(Orange) {} 
int main() { 
   Apple a; 
//! f(a); // Error: ambiguous conversion 
} ///:~

问题:有两种Apple->Orange的方式
解决办法1:

class Orange {}; 
class Pear {}; 
class Apple { 
public: 
   operator Orange() const; 
   operator Pear() const; 
}; 

void eat(Orange); 
void eat(Pear); 
int main() { 
   Apple c; 
//! eat(c); 
   // Error: Apple -> Orange or Apple -> Pear ??? 
}

解决办法2:可以提供转换函数

class Fi {}; 
class Fee { 
public: 
   Fee(int) {} 
   Fee(const Fi&) {} 
}; 
class Fo { 
   int i; 
public: 
   Fo(int x = 0) : i(x) {} 
   operator Fee() const { return Fee(i); } 
}; 
 

int main() { 
  Fo fo; 
  Fee fee = fo; 
}

发生了如下事情:
1)Fo的操作符重载函数调用,返回一个Fee对象
2)Fee没有定义copy构造函数,采用默认的copy构造函数



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值