c++类型转换

c++类型转换


c++提供了特定的强制类型转换:const_cast()、static_cast()、reinterpret_cast()、dynamic_cast()和std::bit_cast()(c++20引入)。

旧的C风格的强制类型转换仍然可以在c++中使用,并且仍在现有代码库中广泛使用。c风格的强制类型转换涵盖了所有4种c++强制类型转换,因此它更容易出错。因为要实现的目标不是很明确,并且最终可能带来意想不到的结果。所以在代码中建议使用c++的强制类型转换,因为他们更安全。

const_cast()

它可以使变量添加或者取消const属性。这是五个类型转换中唯一可以消除const属性的转换。

void func(char *str)
{
    cout << str << endl;
}

void func2(const char *str)
{
    func(const_cast<char *>(str));
}

此外,标准库还提供了名为std::as_const()的辅助方法。该方法定义在<utility>中,该方法接收一个引用参数,返回他的const引用版本。基本上,as_const(obj)等价于const_cast<const T&>(obj),其中T是obj的类型。与使用const_cast()相比,使用as_const()可以使代码更短、更易读。

string str{"hello"};
const string &constStr{as_const(str)};

static_cast()

可以使用static_cast()执行语言直接支持的显示转换,例如,将int转换为double。

int i = 6, j = 7;
double k{static_cast<double>(i) / j};

还可以使用它执行显示转换。这是用户定义的构造函数或转换例程允许的。比如,如果类A有一个接受类B的构造函数,则可以使用static_cast()将B对象转换为A对象。但是,在大多数情况下,这种行为编译器会自动转换。

static_cast()的另外一个用途是在继承层次结构中执行向下强制转换。示例如下:

class Base
{
public:
    Base() = default;
    virtual ~Base() = default;
};

class Derived : public Base
{

public:
    virtual ~Derived() = default;
};
 
int main()
{
    Base *b{nullptr};
    Derived *d{new Derived()};

    b = d; // 向上转型,不需要类型转换
    d = static_cast<Derived *>(b);

    Base base;
    Derived derived;

    Base &br{derived};
    Derived &dr{static_cast<Derived &>(br)};
    return 0;
}

注意:使用static_cast()强制类型转换不会执行运行时的类型检查。所以在某些场景下会造成问题。例如:

Base *b{new Base()};
Derived* d{static_cast<Derived*>(b)};

使用指针d可能会导致潜在的风险,包括在对象边界之外进行内存覆盖。

static_cast()并不是万能的。

  1. 不能将一种类型的指针转换为另一种不相关的类型的指针;
  2. 如果没有可用的转换构造函数,则无法将一种类型的对象直接转换为另一种类型的对象;
  3. 不能将常量类型转换为非常量类型;
  4. 不能将指针转换为整数。

reinterpret_cast()

static_cast()相比,reinterpret_cast()的功能更加强大,但与此同时安全性也将变得更低。它可以执行c++类型规则中不允许的某些强制类型转换,但是在某些情况下对于与开发者是有意义的。比如,即使类型不相关,也可以将一种类型的引用强制转换另一种类型的引用。同样,可以将指针类型强制转换为任何其他指针类型,即使他们与继承层次结构无关。这通常用于将指针强制转换为void*类型的指针(可以隐士完成,不需要强制转换)。但是将void*强制转换会正确的类型的指针需要reinterpret_cast()void*类型的指针只是指向内存中的某个位置。没有类型信息与void*类型的指针相关联。

class X{};
class Y{};

int main()
{
    X x;
    Y y;

    X *xp{&x};
    Y *yp(&y);

    xp = reinterpret_cast<X *>(yp);

    void *p{xp};
    xp = reinterpret_cast<X *>(p);

    X &xr{x};
    Y &yr{reinterpret_cast<Y &>(xr)};

    return 0;
}

reinterpret_cast()并非万能,他对转化还有很多限制。通常应该谨慎使用,因为它允许在不执行任何类型检查的情况下进行转换。

dynamic_cast()

dynamic_cast()提供了对继承层次结构中的强制转换的运行时检查。可以使用它强制转换指针或引用。dynamic_cast()在运行时检查底层对象的运行时类型信息。如果强制转换无效,dynamic_cast()将返回空指针(用于指针),或者抛出std::bad_cast异常(用于引用)

class Base
{
public:
    Base() = default;
    virtual ~Base() = default;
};

class Derived : public Base
{
public:
    virtual ~Derived() = default;
};

int main()
{
    Base *b;
    Derived *d{new Derived()};
    b = d;
    d = dynamic_cast<Derived *>(b);

    Base base;
    Derived derived;

    // 用于引用
    Base &br{base};
    try
    {
        Derived &dr{dynamic_cast<Derived &>(br)};
    }
    catch (const bad_cast &)
    {
        cout << "bad_cast!" << endl;
    }
    return 0;
}

注意:可以使用static_cast()或reinterpret_cast()在继承层次结构上执行相同的强制转换。与dynamic_cast()的区别在于,dynamic_cast()执行运行时(动态)类型检查,而另外两种,则执行强制转换,即使转换的类型是错误的。

运行时的类型信息存储在对象的虚表中。因此要使用dynamic_cast(),类中必须至少要有一个虚方法。如果类中没有虚表,就会导致错误。

std::bit_cast()

c++20引入了std::bit_cast(),定义在<bit>中。这是标准库唯一的强制类型转换,其他强制类型转换是c++语言本身的一部分。bit_cast()与reinterpret_cast()类似。但是它会创建一个指定目标类型的新对象,并按位从源对象复制到此新对象。它有效的将源对象的位解释为目标对象的位。bit_cast()要求源对象和目标对象的大小相同,并且两者都是可以复制的。示例:

float asFloat{1.23f};
auto asUint{bit_cast<unsigned int>(asFloat)};
if (bit_cast<float>(asUint) == asFloat)
{
    cout << "success!" << endl;
}

bit_cast的一个用例是普通可复制类的二进制I/O。比如,可以将此类型的各个字节写入文件。当文件读回到内存时,可以使用bit_cast()正确的解释从文件中读取的字节。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值