C++ 高级篇(四)—— 类型转换高级

目前为止,我们一直使用传统的类型转换符来进行简单对象的类型转换。例如,要把一个double类型的浮点型数字转换为int 的整型数字,我们是这样做的:

int i;
double d;
i = (int) d;

或者

i = int (d);

这样做对基本数据类型时没问题的,因为基本数据类型的转换已经有标准的定义。同样的操作也可以被在类或类的指针上,因此以下例子中的写法也是没有问题的:

    // class type-casting
    #include <iostream.h>
    
    class CDummy {
        int i;
    };
    
    class CAddition {
        int x,y;
      public:
        CAddition (int a, int b) { x=a; y=b; }
        int result() { return x+y;}
    };
    
    int main () {
        CDummy d;
        CAddition * padd;
        padd = (CAddition*) &d;
        cout << padd->result();
        return 0;
    }
		
 

虽然以上程序在C++中是没有语法错误的(多数编译器甚至不会产生警告信息),但这段程序没有什么实际的逻辑意义。我们使用了CAddition 的成员函数result 而没有定义一个相应的该类的对象:padd 并不是一个对象,它只是一个指针,被我们赋值指向一个毫无关系的对象的地址。当在程序运行到访问它的result 成员函数时,将会有一个运行错误(run-time error)产生,或生成一个意外的结果。

为了控制这种类之间的转换,ANSI-C++ 标准定义了4种新的类型转换操作符: reinterpret_cast, static_cast, dynamic_cast 和 const_cast。所有这些操作符都是同样的使用格式:

reinterpret_cast <new_type> (expression)
dynamic_cast <new_type> (expression)
static_cast <new_type> (expression)
const_cast <new_type> (expression)

这里new_type 是要转换成的目标类型,expression 是要被转换的内容。为了便于理解,模仿传统转换操作符,它们的含义是这样的:

(new_type) expression
new_type (expression) 

 

reinterpret_cast

 

reinterpret_cast 可以将一个指针转换为任意其它类型的指针。它也可以用来将一个指针转换为一个整型,或反之亦然。

这个操作符可以在互不相关的类之间进行指针转换,操作的结果是简单的将一个指针的二进制数据(binary copy)复制到另一个指针。对指针指向的内容不做任何检查或转换。

如果这种复制发生在一个指针到一个整数之间,则对其内容的解释取决于不同的系统,因此任何实现都是不可移植(non portable)的。一个指针如果被转换为一个能够完全存储它的足够大的整数中,则是可以再被转换回来成为指针的。

例如:

class A {};
class B {};
A * a = new A;
B * b = reinterpret_cast<B*>(a);

reinterpret_cast 对所有指针的处理与传统的类型转换符所作的一模一样。

 

static_cast

 

static_cast 可以执行所有能够隐含执行的类型转换,以及它们的反向操作(即使这种方向操作是不允许隐含执行的)。

用于类的指针,也就是说,它允许将一个引申类的指针转换为其基类类型(这是可以被隐含执行的有效转换),同时也允许进行相反的转换:将一个基类转换为一个引申类类型。

在后面一种情况中,不会检查被转换的基类是否真正完全是目标类型的。例如下面的代码是合法的:

class Base {};
class Derived: public Base {};
Base * a = new Base;
Derived * b = static_cast(a);

static_cast除了能够对类指针进行操作,还可以被用来进行类中明确定义的转换,以及对基本类型的标准转换:

double d=3.14159265;
int i = static_cast<int>(d);

译者注:如果你对这部分看不太懂,请结合下面的dynamic_cast一起看,也许会帮助理解。

 

dynamic_cast

 

dynamic_cast 完全被用来进行指针的操作。它可以用来进行任何可以隐含进行的转换操作以及它们被用于多态类情况下的方向操作。然而与static_cast不同的是, dynamic_cast 会检查后一种情况的操作是否合法,也就是说它会检查类型转换操作是否会返回一个被要求类型的有效的完整的对象。

这种检查是在程序运行过程中进行的。如果被转换的指针所指向的对象不是一个被要求类型的有效完整的对象,返回值将会是一个空指针NULL 。

   class Base { virtual dummy(){}; };
   class Derived : public Base { };
   

   Base* b1 = new Derived;
   Base* b2 = new Base;
   Derived* d1 = dynamic_cast(b1);   // succeeds
   Derived* d2 = dynamic_cast(b2);   // fails: returns NULL
   

如果类型转换被用在引用(reference)类型上,而这个转换不可能进行的话,一个bad_cast 类型的例外(exception)将会被抛出:

  class Base { virtual dummy(){}; };
  class Derived : public Base { };
  
  Base* b1 = new Derived;
  Base* b2 = new Base;
  Derived d1 = dynamic_cast(b1);   // succeeds
  Derived d2 = dynamic_cast(b2);   // fails: exception thrown
  

 

const_cast

 

这种类型转换对常量const 进行设置或取消操作:

class C {};
const C * a = new C;
C * b = const_cast<C*> (a);

其他3种cast 操作符都不可以修改一个对象的常量属性(constness)。

 

typeid

 

ANSI-C++ 还定义了一个新的操作符叫做 typeid ,它检查一个表达式的类型:

typeid (expression)

这个操作符返回一个类型为type_info的常量对象指针,这种类型定义在标准头函数中。这种返回值可以用操作符 == 和 != 来互相进行比较,也可以用来通过name()函数获得一个描述数据类型或类名称的字符串,例如:

    // typeid, typeinfo
    #include <iostream.h>
    #include <typeinfo>
    
    class CDummy { };
    
    int main () {
        CDummy* a,b;
        if (typeid(a) != typeid(b)) {
            cout << "a and b are of different types:\n";
            cout << "a is: " << typeid(a).name() << '\n';
            cout << "b is: " << typeid(b).name() << '\n';
        }
        return 0;
    }
在国际象棋中,学会如何移动棋子只能算是入门:要想掌控整个棋局,我们必须了解自己所下的每一步棋后的策略和战术。在C++中也同样如此。掌握正确的策略可以帮助我们避免常见的陷阱,并提高我们的工作效率。在本书中,C++专家Rob Murray就与我们分享了他宝贵的经验和建议,以帮助初中级C++程序员得到进一步的提高。  在本书中,作者大量采用了实际开发中的代码来作为示例,向读者展示了那些有用的编程策略,并对那些有害的做法进行了警示。为了帮助读者更好地理解,在书中的每一章结束前,在该章中被介绍过的主要内容都被放到了一个列表中,此外,书中还给出了一些问题来激励读者们进行更多的思考和讨论。 本书在一开始就向我们讲解了如何为我们的设计选择正确的抽象,提示我们注意抽象和现实之间的区别。然后,我们就将学到如何将已得到的抽象转化成一个(或多个)C++中的类,期间进行的讨论所涵盖的范围上至高层的设计策略,下至底层的接口和实现细节。   接下来本书对单继承和多重继承进行了深入的探索。一开始书中会给出一个关于它们应该用在设计的什么地方的讨论,然后就是一些详细的示例代码,用来向我们演示如何在实践中使用这些概念。对于“如何构建可派生出其他类的类”以及“这么做的好处何在”,书中还专门抽出了一章来讨论它们。   对于C++中新增的模板特性,通过从基础开始到逐步地接触实际应用中的示例,Rob Murray向我们展示了其空前的洞察力。作者同时也向我们展示了多种特定的技巧,以使我们的程序更快、重用性更高,并且更健壮。异常是C++中另外一个新增的特性,对于何时该使用它,何时不该使用它,Murray也向我们给出了他的建议。在本书的最后,我们还可以学到如何将一个项目从C移植到C++之上,书中对该过程的讨论不但包括了其中可能出现的技术问题,也包括了使用技术的“人”的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值