C++类型转换
我认为,C++中类型转换是函数重载甚至是函数模板自动推演的基础,它是选择候选函数的依据。所以,下面就唠叨一下吧。这里不讨论具体的转换方法,比如怎么从string转换成int,这里主要讨论的是语言方面的理论,大部分内容在c++ primer里都可以找到。
首先,类型转换分成隐式类型转换和显式类型转换两种。primer中又把算术转换和旧式强制类型转换独立出来了,它们还是分别属于隐式和显式两种的。对于内置类型之间的转换就没有啥说的了(STL中的转换函数也已经实现的很完整了)。复杂的地方在于自定义类类型的类型转换上面。在没有特殊设计的情况下,自定义类型和其他类型之间是没有转换关系的,只有设计好相应的转换函数,编译器才可以实现隐式或者显式的类型转换。这个设计就是用户定义的转换。
用户定义转换可以通过两种途径完成。
1. 用户定义的转换函数: 如果想让MyInt转成MyDouble,只需要在MyInt类中定义转换函数即可,如下:
#include <stdafx.h> #include <iostream> using namespace std; class MyDouble{ public: MyDouble():data(0){} MyDouble& operator=(const MyDouble it){ cout<< "MyDouble assign operator called!" << endl; if( &it != this ) { data = it.data; } return *this; } double data; }; class MyInt{ public: MyInt():data(0){} // 用户定义的转换函数 MyInt->MyDouble operator MyDouble(){ cout<< "Converting MyInt->MyDouble by convert func in MyInt" << endl; MyDouble db; db.data = data; return db; } int data; }; int main() { MyInt it; MyDouble db; it.data = 10; db = it; //it由MyInt转换成MyDouble之后调用了赋值操作函数 db.data += 0.1; cout<< db.data << endl; cin.get(); return 0; }
运行结果:
Converting MyInt->MyDouble by convert func in MyInt MyDouble assign operator called! 10.1
2. 通过单参数的构造函数: 如果想让MyInt转成MyDouble, 只需要在类MyDouble函数中定义一个参数为MyInt对象或引用的构造函数。如下:
#include <stdafx.h> #include <iostream> using namespace std; class MyInt{ public: MyInt():data(0){} int data; }; class MyDouble{ public: MyDouble():data(0){} MyDouble(const MyInt& it) :data(it.data) { cout<< "Converting MyInt->MyDouble by constructure in MyDouble" << endl; // nothing } MyDouble& operator=(const MyDouble it){ cout<< "MyDouble assign operator called!" << endl; if( &it != this ) { data = it.data; } return *this; } double data; }; int main() { MyInt it; MyDouble db; it.data = 10; db = it; //it由MyInt转换成MyDouble之后调用了赋值操作函数 db.data += 0.1; cout<< db.data << endl; cin.get(); return 0; }
运行结果:
Converting MyInt->MyDouble by constructure in MyDouble MyDouble assign operator called! 10.1
两个例子很简单,所以说,如果想做类型转换A->B,可以再A中定义转换函数,也可以在B中定义单参构造函数。但是第二种转换方式总是被人们所忽略,而非常有可能由此产生一些很诡异的问题。基于此,本公司的coding convention明确的说明要在单参构造函数前面加上explicit(显式)来禁用编译器的自动类型转换。
隐式类型转换的过程:
上面说的就是用户定义的转换,当然相对应的还有标准转换,说白了就是系统已经内置的转换。需要注意的是,除了我们常见的内置类型之间的标准转换之外。类类型之间Derived Class向Base Class的转换、Derived Class pointer向Base Class pointer的转换以及类类型指针向void指针的转换也都属于标准转换!
隐式类型转换就是在没有人工干预的情况下,编译器自动执行的一种类型转换。当然了,两个类型之间一定要有已经定义好的直接或者间接的转换关系。那么有人会问,如果A可以转换成B,B可以转换成C,C可以转换成D。那么可以由A直接转换成D么?答案是NO!编译器的自动转换也是有限度的。在一次转换过程中,编译器只进行不超过一次的标准转换和不超过一次的用户定义转换,可以只有标准转换或者只有自定义转换。
另外,从A类型到B类型可能存在多个转换序列,在转换的过程中,编译器会选择一个最优的,原则嘛,就是 精确匹配>算术提升>标准转换>用户定义转换。 如果存在两个转换序列具有相同的级别,就称作转换有二义性,会报错。解决的方法就是使用显示的转换,强制进行某个类型转换。
强制类型转换
C++中引入了一系列强制转换标号,同时也兼容C风格的()强制类型转换。记一下C++中几个强制类型转换的用法吧。
const_cast: 功能简单,将施加在变量上的const修饰去掉。
static_cast: 最通用的类型转换运算符,可以转换指针,类。如果两个转换类型之间存在自定义的转换函数,相应的转换函数会自动调用。最通用的东西往往不是最强大的。
dynamic_cast: 用户存在继承关系的多个类指针或void *之间转换。它的操作对象一定是类指针!它的优点是具有类型检查的功能,如果动态类型和静态类型不匹配,会抛出一个bad_cast的异常。这是static_cast所没有的!
reinterpret_cast:意思就是不做编译器默认操作的类型转换,不推荐使用。和static_cast的区别是:在多继承环境中,把derived指针转换成base指针的时候,因为对象内存结构的因素导致转换成的base指针地址和原来的derived指针地址不同,这是编译器内部完成的,这个过程static_cast可以正常进行,而reinterpret_cast不会做这个事情,它只会粗暴的把derived指针地址传给base指针变量。当然了,这个规则和编译器有关,万恶VS2010中没有这个规则(多继承中,转换之后所有的base指针值都和derived指针一样)!《深入理解C++对象模型》和《Effective C++》中都有相关的例子,但是在VS2010中是试不出来的!找个G++试吧!
贴点儿相关代码:
#include <stdafx.h> #include <iostream> #include <string> using namespace std; class MyInt{ public: MyInt():data(0){} int data; }; class MyDouble{ public: MyDouble():data(0){} MyDouble(const MyInt& it) :data(it.data) { cout<< "Converting MyInt->MyDouble by constructure in MyDouble" << endl; // nothing } MyDouble& operator=(const MyDouble it){ cout<< "MyDouble assign operator called!" << endl; if( &it != this ) { data = it.data; } return *this; } double data; }; class BaseA{ public: string base; }; class BaseB{ public: virtual void print(){} string base; }; class Derived : public BaseA, public BaseB{ public: virtual void print(){} string derived; }; int main() { MyInt it; MyDouble db; Derived d; // VS2010中得到的结果都一样!!shit! printf("%p %p %p\n", &d, reinterpret_cast<BaseA*>(&d), reinterpret_cast<BaseB*>(&d) ); it.data = 10; db = static_cast<MyDouble>(it); //it由MyInt转换成MyDouble之后调用了赋值操作函数 db.data += 0.1; cout<< db.data << endl; cin.get(); return 0; }
posted on 2012-10-19 11:10 William.Wu 阅读(380) 评论(0) 编辑 收藏 举报