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

来源:互联网 发布:曹雪松java百度网盘 编辑:程序博客网 时间:2024/06/15 13:00

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构造函数