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编辑  收藏  举报

导航