• 16.2. Boost.Conversion

    16.2. Boost.Conversion

    Boost.Conversion 库由两个文件组成。分别在 boost/cast.hpp 文件中定义了 boost::polymorphic_castboost::polymorphic_downcast 这两个类型转换操作符, 在 boost/lexical_cast.hpp 文件中定义了 boost::lexical_cast

    boost::polymorphic_castboost::polymorphic_downcast 是为了使原来用 dynamic_cast 实现的类型转换更加具体。具体细节,如下例所示。

    1. struct father
    2. {
    3. virtual ~father() { };
    4. };
    5.  
    6. struct mother
    7. {
    8. virtual ~mother() { };
    9. };
    10.  
    11. struct child :
    12. public father,
    13. public mother
    14. {
    15. };
    16.  
    17. void func(father *f)
    18. {
    19. child *c = dynamic_cast<child*>(f);
    20. }
    21.  
    22. int main()
    23. {
    24. child *c = new child;
    25. func(c);
    26.  
    27. father *f = new child;
    28. mother *m = dynamic_cast<mother*>(f);
    29. }
    • 下载源代码

    本例使用 dynamic_cast 类型转换操作符两次: 在 func() 函数中,它将指向父类的指针转换为指向子类的指针。在 main() 中, 它将一个指向父类的指针转为指向另一个父类的指针。第一个转换称为向下转换(downcast),第二个转换称为交叉转换(cross cast)。

    通过使用 Boost.Conversion 的类型转换操作符,可以将向下转换和交叉转换区分开来。

    1. #include <boost/cast.hpp>
    2.  
    3. struct father
    4. {
    5. virtual ~father() { };
    6. };
    7.  
    8. struct mother
    9. {
    10. virtual ~mother() { };
    11. };
    12.  
    13. struct child :
    14. public father,
    15. public mother
    16. {
    17. };
    18.  
    19. void func(father *f)
    20. {
    21. child *c = boost::polymorphic_downcast<child*>(f);
    22. }
    23.  
    24. int main()
    25. {
    26. child *c = new child;
    27. func(c);
    28.  
    29. father *f = new child;
    30. mother *m = boost::polymorphic_cast<mother*>(f);
    31. }
    • 下载源代码

    boost::polymorphic_downcast 类型转换操作符只能用于向下转换。 它内部使用 static_cast 实现类型转换。 由于 static_cast 并不动态检查类型转换是否合法,所以 boost::polymorphic_downcast 应该只在类型转换是安全的情况下使用。 在调试(debug builds)模式下, boost::polymorphic_downcast 实际上在 assert ()函数中使用 dynamic_cast 验证类型转换是否合法。 请注意这种合法性检测只在定义了NDEBUG宏的情况下执行,这通常是在调试模式下。

    向下转换最好使用 boost::polymorphic_downcast, 那么 boost::polymorphic_cast 就是交叉转换所需要的了。 由于 dynamic_cast 是唯一能实现交叉转换的类型转换操作符,boost::polymorphic_cast 内部使用了它。 由于 boost::polymorphic_cast 能够在错误的时候抛出 std::bad_cast 类型的异常,所以优先使用这个类型转换操作符还是很有必要的。相反,dynamic_cast 在类型转换失败使将返回0。 避免手工验证返回值,boost::polymorphic_cast 提供了自动化的替代方式。

    boost::polymorphic_downcastboost::polymorphic_cast 只在指针必须转换的时候使用;否则,必须使用 dynamic_cast 执行转换。 由于 boost::polymorphic_downcast 是基于 static_cast,所以它不能够,比如说,将父类对象转换为子类对象。 如果转换的类型不是指针,则使用 boost::polymorphic_cast 执行类型转换也没有什么意义,而在这种情况下使用 dynamic_cast 还会抛出一个 std::bad_cast 异常。

    虽然所有的类型转换都可用 dynamic_cast 实现,可 boost::polymorphic_downcastboost::polymorphic_cast 也不是真正随意使用的。 Boost.Conversion 还提供了另外一种在实践中很有用的类型转换操作符。 体会一下下面的例子。

    1. #include <boost/lexical_cast.hpp>
    2. #include <string>
    3. #include <iostream>
    4.  
    5. int main()
    6. {
    7. std::string s = boost::lexical_cast<std::string>(169);
    8. std::cout << s << std::endl;
    9. double d = boost::lexical_cast<double>(s);
    10. std::cout << d << std::endl;
    11. }
    • 下载源代码

    类型转换操作符 boost::lexical_cast 可将数字转换为其他类型。 例子首先将整数169转换为字符串,然后将字符串转换为浮点数。

    boost::lexical_cast 内部使用流(streams)执行转换操作。 因此,只有那些重载了 operator<<()operator>>() 这两个操作符的类型可以转换。 使用 boost::lexical_cast 的优点是类型转换出现在一行代码之内,无需手工操作流(streams)。 由于流的用法对于类型转换不能立刻理解代码含义, 而 boost::lexical_cast 类型转换操作符还可以使代码更有意义,更加容易理解。

    请注意 boost::lexical_cast 并不总是访问流(streams);它自己也优化了一些数据类型的转换。

    如果转换失败,则抛出 boost::bad_lexical_cast 类型的异常,它继承自 std::bad_cast

    1. #include <boost/lexical_cast.hpp>
    2. #include <string>
    3. #include <iostream>
    4.  
    5. int main()
    6. {
    7. try
    8. {
    9. int i = boost::lexical_cast<int>("abc");
    10. std::cout << i << std::endl;
    11. }
    12. catch (boost::bad_lexical_cast &e)
    13. {
    14. std::cerr << e.what() << std::endl;
    15. }
    16. }
    • 下载源代码

    本例由于字符串 "abc" 不能转换为 int 类型的数字而抛出异常。