c++中的类型转换

作者:jicanmeng

时间:2017年06月20日


In C++, there are 5 different types of casts: C-style casts, static casts, const casts, dynamic casts, and reinterpret casts.

Const casts and reinterpret casts should generally be avoided because they are only useful in rare cases and can be harmful if used incorrectly. 本文不会讨论这两种转换。

传统的C-style类型转换的缺点如下:

Because C-style casts are not checked by the compiler at compile time, C-style casts can be inherently misused, because they will let you do things that may not make sense, such as getting rid of a const or changing a data type without changing the underlying representation (leading to garbage results). Consequently, C-style casts should generally be avoided.

不过在我看来,C-style和static_cast也没啥区别啊,都是告诉编译器进行显式的类型转换。如下面的例子:

#include <iostream>

int main()
{
    float a = 3.2f;
//    int b = (int)a;
    int b = static_cast<int>(a);

    std::cout << b << std::endl;
    return 0;
}

第6行和第7行都可以。如果不加类型转换,会有警告:warning C4244: “初始化”: 从“float”转换到“int”,可能丢失数据

dynamic_cast最常见的用法是将指向父类的指针进行转换,然后赋值给指向子类的指针。

#include <iostream>
#include <string>

class Base
{
protected:
    int m_value;

public:
    Base(int value)
        : m_value(value)
    {
    }
    virtual ~Base() {}
};

class Derived : public Base
{
protected:
    std::string m_name;

public:
    Derived(int value, std::string name)
        : Base(value), m_name(name)
    {
    }

    const std::string& getName() { return m_name; }
};

Base* getObject(bool bReturnDerived)
{
    if (bReturnDerived)
        return new Derived(1, "Apple");
    else
        return new Base(2);
}

int main()
{
    Base *b = getObject(true);

    Derived *d = dynamic_cast(b); // use dynamic cast to convert Base pointer into Derived pointer

    if (d) {
        std::cout << "The name of the Derived is: " << d->getName() << '\n';
    }

    delete b;

    return 0;
}

以下是运行结果:

[jicanmeng@andy tmp]$ ./a.out
				The name of the Derived is: Apple
			[jicanmeng@andy tmp]$

dynamic_cast也可以用于引用。将上面的程序稍微修改一下:

#include <iostream>
#include <string>

class Base
{
protected:
    int m_value;

public:
    Base(int value)
        : m_value(value)
    {
    }
    virtual ~Base() {}
};

class Derived : public Base
{
protected:
    std::string m_name;

public:
    Derived(int value, std::string name)
        : Base(value), m_name(name)
    {
    }

    const std::string& getName() { return m_name; }
};

Base* getObject(bool bReturnDerived)
{
    if (bReturnDerived)
        return new Derived(1, "Apple");
    else
        return new Base(2);
}

int main()
{
    Base *b = getObject(true);

    Derived &d = dynamic_cast<Derived>(*b); // use dynamic cast to convert Base reference into Derived reference

    std::cout << "The name of the Derived is: " << d.getName() << '\n';

    delete b;

    return 0;
}

learncpp.com上面是这样说的:

Because C++ does not have a “null reference”, dynamic_cast can’t return a null reference upon failure. Instead, if the dynamic_cast of a reference fails, an exception of type std::bad_cast is thrown.

所以,对于上面的这个程序,如果在调用getObject()函数时传递的是false,那么程序就会运行终止并退出。可以使用前面文章中提到的"exception handling"来进行处理。比如修改为如下的形式:

#include <iostream>
#include <string>

class Base
{
protected:
    int m_value;

public:
    Base(int value)
        : m_value(value)
    {
    }
    virtual ~Base() {}
};

class Derived : public Base
{
protected:
    std::string m_name;

public:
    Derived(int value, std::string name)
        : Base(value), m_name(name)
    {
    }

    const std::string& getName() { return m_name; }
};

Base* getObject(bool bReturnDerived)
{
    if (bReturnDerived)
        return new Derived(1, "Apple");
    else
        return new Base(2);
}

int main()
{
    Base *b = getObject(false);

    try {
        Derived &d = dynamic_cast<Derived>(*b); // use dynamic cast to convert Base reference into Derived reference
        std::cout << "The name of the Derived is: " << d.getName() << '\n';
    }
    catch (std::bad_cast) {
        std::cout << "bad cast from Base to Derived" << std::endl;
    }

    delete b;

    return 0;
}

以下是运行结果:

[jicanmeng@andy tmp]$ ./a.out
				bad cast from Base to Derived
				请按任意键继续. . .
			[jicanmeng@andy tmp]$

对于static_cast和dynamic_cast有如下的简单总结:

  1. dynamic_cast:运行时检查,用于多态的类型转换(upcast,downcast和crosscast),只能转换指针和引用。
  2. static_cast:编译时检查,用于非多态的转换,可以转换指针及其他,比如:int->float。
  3. 使用dynamic_cast转换成子类时,基类中必须有虚函数,才不会报错,否则编译失败(因为dynamic_cast是运行时检查类型,而这个类型信息存储在虚函数表中),使用static_cast转换时,即使基类没有虚函数,编译也不会报错。

参考资料

  1. <<C++实用教程>> 电子工业出版社 郑阿奇 主编 丁有和 编著
  2. The C++ Tutorial:
    http://www.learncpp.com/cpp-tutorial/4-4a-explicit-type-conversion-casting/
    http://www.learncpp.com/cpp-tutorial/12-9-dynamic-casting/
  3. misc:
    http://www.cnblogs.com/QG-whz/p/4509710.html
    http://www.cnblogs.com/ider/archive/2011/07/30/cpp_cast_operator_part3.html http://568464209.blog.51cto.com/7726521/1304091/