C++“准”标准库Boost学习指南(2):Boost.Conversion

Conversion库包含有一些函数,它们是现有的强制类型转换操作符(static_cast, const_cast, 和 dynamic_cast)的增强。Conversion为安全的多态转换增加了 polymorphic_cast 和 polymorphic_downcast,为安全的数字类型转换增加了 numeric_cast,为文本转换(如string 和 double间的转换)增加 lexical_cast。你可为了你自己的类型更好地工作而定制这些类型转换,可能这些类型并不可以使用语言本身所提供的类型转换。

Conversion 库如何改进你的程序?
  • 可理解、可维护,以及一致的多态类型转换
  • 静态向下转型使用比static_cast更安全的结构
  • 进行范围判断的数字转换确保正确的值逻辑以及更少的调试时间
  • 正确且可重用的文字转换导致更少的编码时间

C++的多功能性是它获得成功的主要原因之一,但有时也是麻烦的来源,因为语言各部分的复杂性。例如,数字转换规则以及类型提升规则都很复杂。其它转换虽然简单,但也很乏味;多少次我们需要写一个安全的函数来进行strings 和 ints, doubles 和 strings之间的转换?在你写的每个库和程序里,类型转换都可能是有问题的,这就是 Conversion 库可以帮助你的地方。它提供了防止危险转换及可复用的类型转换工具。

Conversion 库由四个转换函数组成,分别提供了更好的类型安全性(polymorphic_cast), 更高效的类型安全防护(polymorphic_downcast), 范围检查的数字转换(numeric_cast), 以及文字转换(lexical_cast)。这些类cast函数共享C++转型操作符的语义。与C++的转型操作符一样,这些函数具有一个重要的品质,类型安全性,这是它们与C风格转型的区别:它们明确无误地表达了程序员的意图。我们所写的代码的重要性不仅在于它可以正确执行。更重要的是代码可否清晰地表达我们的意图。这个库使得我们可以更容易地扩展我们的C++词汇表。


polymorphic_cast

头文件: "boost/cast.hpp"

C++中的多态转型是用 dynamic_cast来实现的。dynamic_cast有一个有时会导致错误代码的特性,那就是它对于所使用的不同类型会有不同的行为。在用于一个引用类型时,如果转型失败,dynamic_cast 会抛出一个std::bad_cast异常。这样做的原因很简单,因为C++里不允许有空的引用,所以要么转型成功,要么转型失败而你获得一个异常。当然,在 dynamic_cast 用于一个指针类型时,失败时将返回空指针。

dynamic_cast的这种对指针和引用类型的不同行为以前被认为是一个有用的特性,因为它允许程序员表达他们的意图。典型地,如果转型失败不是一种逻辑错误,就使用指针转型,如果它确是一种错误,就使用引用转型。不幸的是,两种方法之间的区别仅在于一个*号和一个&号,这种细微的差别是不自然的。如果想把指针转型失败作为错误处理,该怎么办?为了通过自动抛出异常来清楚地表达这一点,也为了让代码更一致,Boost提供了polymorphic_cast. 它在转型失败时总是抛出一个 std::bad_cast 异常。

在《The C++ Programming Language 3rd Edition》中,Stroustrup对于指针类型的dynamic_cast说了以下一段话,事实是它可以返回空指针:
"偶尔可能会不小心忘了测试指针是否为空。如果这困扰了你,你可以写一转型函数在转型失败时抛出异常。"

polymorphic_cast 正是这样一个转型函数。

用法

polymorphic_cast 的用法类似于 dynamic_cast, 除了 (正是它的意图) 在转型失败时总是抛出一个 std::bad_cast 异常。polymorphic_cast 的另一个特点是它是一个函数,必要时可以被重载。作为对我们的C++词汇表的一个自然扩展,它使得代码更清晰,类型转换也更少错误。要使用它,就要包含头文件"boost/cast.hpp". 这个函数泛化了要转换的类型,并接受一个要进行转型的参数。

 template <class Target, class Source>

 polymorphic_cast(Source* p);
要注意的是,polymorphic_cast 没有针对引用类型的版本。原因是那是dynamic_cast已经实现了的,没有必须让 polymorphic_cast 重复C++语言中已有的功能。以下例子示范了与 dynamic_cast类似的语法。

向下转型和交叉转型

使用dynamic_cast 或 polymorphic_cast可能有两种典型的情况:从基类向派生类的向下转型,或者交叉转型,即从一个基类到另一个基类。以下例子示范了使用polymorphic_cast的两类转型。这里有两个基类,base1 和 base2, 以及一个从两个基类公有派生而来的类 derived 。

#include <iostream>
#include <string>
#include "boost/cast.hpp"

class base1 {
public:
  virtual void print() {
    std::cout << "base1::print()\n";
  }

  virtual ~base1() {}
};

class base2 {
public:

  void only_base2() {
    std::cout << "only_base2()\n";
  }

  virtual ~base2() {}
};

class derived : public base1, public base2 {
public:

  void print() {
    std::cout << "derived::print()\n";
  }

  void only_here() {
    std::cout << "derived::only_here()\n";
  }
  void only_base2() {
    std::cout << "Oops, here too!\n";
  }
};

int main() {
  base1* p1=new derived;

 p1->print();

  try {
    derived* pD=boost::polymorphic_cast<derived*>(p1);
    pD->only_here();
    pD->only_base2();

    base2* pB=boost::polymorphic_cast<base2*>(p1);
    pB->only_base2();

  }
  catch(std::bad_cast& e) {
    std::cout << e.what() << '\n';
  }

  delete p1;
}


我们来看看 polymorphic_cast 是如何工作的,首先我们创建一个 derived 的实例,然后通过不同的基类指针以及派生类指针来操作它。对p1使用的第一个函数是print, 它是base1 和 derived的一个虚拟函数。我们还使用了向下转型,以便可以调用 only_here, 它仅在 derived中可用:

derived* pD=boost::polymorphic_cast<derived*>(p1);

pD->only_here();
注意,如果 polymorphic_cast 失败了,将抛出一个 std::bad_cast 异常,因此这段代码被保护在一个 try/catch 块中。这种做法与使用引用类型的dynamic_cast正好是一样的。指针 pD 随后被用来调用函数 only_base2. 这个函数是base2中的非虚拟函数,但是在derived中也提供了,因此隐藏了base2中的版本。因而我们需要执行一个交叉转型来获得一个base2指针,才可以调用到 base2::only_base2 而不是 derived::only_base2.

base2* pB=boost::polymorphic_cast<base2*>(p1);

pB->only_base2();
再一次,如果转型失败,将会抛出异常。这个例子示范了如果转型失败被认为是错误的话,使用polymorphic_cast可以多容易地进行错误处理。不需要测试空指针,也不会把错误传播到函数以外。正如我们即将看到的,dynamic_cast 有时会为这类代码增加不必要的复杂性;它还可能导致未定义行为。

dynamic_cast 对 polymorphic_cast

为了看一下这两种转型方法之间的不同,我们把它们放在一起来比较一下复杂性。我们将重用前面例子中的类 base1, base2, 和 derived。你会发现在对指针类型使用dynamic_cast时,测试指针的有效性是一种既乏味又反复的事情,这使得测试很容易被紧张的程序员所忽略掉。

void polymorphic_cast_example(base1* p) {
  derived* pD=boost::polymorphic_cast<derived*>(p);
  pD->print();

  base2* pB=boost::polymorphic_cast<base2*>(p);
  pB->only_base2();
}

void dynamic_cast_example(base1* p) {
  derived* pD=dynamic_cast<derived*>(p);
  if (!pD)
    throw std::bad_cast();
  pD->print();

  base2* pB=dynamic_cast<base2*>(p);
  if (!pB)
    throw std::bad_cast();

  pB->only_base2();

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值