int 类型的实参与 const char * 类型的形参不兼容_C++类型转换(收藏)

C++为了规范C中的类型转换,加强类型转换的可视性,引入了四种强制类型转换操作符:static_cast, reinterpret_cast, const_cast, dynamic_cast
他们本质上都是模板类。

下面分别来介绍:

1.static_cast

它用于非多态类型的转换(静态转换),对应于C中的隐式类型转换,但他不能用于两个不相关类型的转换,如整形和整形指针之间的转换,虽然二者都是四个字节,但他们一个表示数据,一个表示地址,类型不相关,无法进行转换。

void Test()
{
    //C中的方式
    int i = 10;
    double d1 = i;//隐式类型转换
    //int *p = i;//无法隐式类型转换,只能强制类型转换
    int *p = (int*)i;

    //C++中的方式
    double d2 = static_cast<double>(i);
    //相当于创建一个static_cast<double>类型的匿名对象赋值给d2
    int* p2 = static_cast<int*>(i);//无法转换,会报错
}

e01a4aa26b62394c4e02934ba468614c.png


有这么一种特殊的情况:

class A
{
public:
    A(int a)
    :_a(a)
    {}
private:
    int _a;
};
void test()
{
    A a1(10);//调用构造函数创建A类对象a1
    A a2 = 20;
    A a3 = static_cast<A>(20);
}

对于A a2 = 20;这一行代码,等号右边是一个整型,左边是自定义A类型,两者类型完全不相关,理论上是不能进行转换的,但是编译器却没有报错,原因是A类型的构造函数只有一个参数,编译器会用20构造一个A类型的临时对象,再调用拷贝构造函数创建a2,由于在一条表达式中既有构造又有拷贝构造,编译器会进行优化,将二者合二为一,只调用了构造函数。因此,单参的构造函数会进行隐式类型转换,C++中即可用static_cast来转。但是这种隐式类型转换有时候是不安全的,尤其是在使用智能指针时,当我们将一个不需要用户自己管理释放空间的指针交给智能指针时,系统并不会报错,但是智能能指针使用完会调用析构函数来释放空间,此时就会出问题。
为了防止这种单参构造函数的隐式类型转换,C++提供了一个关键字 explicit来消除这种转换。
加上explicit之后:

class A
{
public:
    explicit A(int a)
    :_a(a)
    {}
private:
     int _a;
};
void test()
{
    A a1(10);
    A a2 = 20;
}

858f74c15c36af3f45cceaa0cec92bef.png


这样就有效的避免了隐不合时宜的转换。

2.reinterpret_cast

reinterpret的含义是重新解释,可将一种类型转换成另一种不相关类型,对应C中的强制类型转换,处理无法进行隐式转换的情况

void Test()
{
    int i = 10;
    int* p2 = reinterpret_cast<int*>(i);
}

强制类型转换有时可以很暴力的处理一些问题
如下例:
对于一个带参数的函数,如何不传参也可以调用该函数?

void Fun(int s)
{
    cout << s << endl;
}

typedef void(*FUNC)();

void Test()
{
    FUNC pf = reinterpret_cast<FUNC>(Fun);
    pf();
}

C中的强制类型转换也可以处理。
虽然我们通过这种BUG的方式转换函数指针,但是这样的代码是不可移植的,而且有时会产生不确定的结果,所以不建议这样来用
如此处输出的s的值就为一个随机值,虽然用户在外部未传参,但是该函数在调用时会创建形参,该形参未初始化,自然是随机值

db6a490af987d6ca952ad136ebbeb84a.png

3.const_cast

他的功能就是删除变量的const属性,方便再次赋值

void Test3()
{
    const int i = 10;
    int *p = const_cast<int*>(&i);
    *p = 20;

    cout << i << endl;
    cout << *p << endl;
}

此时这两个值会分别输出多少呢
我们先来看下监视窗口:

9d1e4cea857eba3ef5b9d7793a5d5dc5.png


可是输出结果却是这样的:

943d547e84a63e9dd8e47e3af98b23ec.png

事实上,一旦将一个变量定义成const类型,编译器会进行优化,将该值放在寄存器中,默认他是不能被改变的,每次在使用时直接从寄存器中取数据,即使我们改变了他在内存中的值,编译器对此是透明的,无法看到

查看汇编代码:

766d5d36f1eb15f9a4117fbea5dbff78.png

这里编译器的处理更为直接,将0Ah直接压栈,所以结果是这样的

为了防止编译器对我们的代码进行优化,我们可以借助一个关键字valatile
他可以保证每次从内存中进行取数据。

4.dynamic_cast

这种转换用于将父类对象的指针转换成子类对象的指针或引用。
向上转型:子类对象指针->父类指针/引用(不需要转换,赋值兼容规则)
向下转型:父类对象指针->子类指针/引用(用dynamic_cast转型是安全的)

class A
{
public:
    virtual void f()
    {
        cout << "A::f()" << endl;
    }
    int _a;
};
class B:public A
{
public:
    virtual void f()
    {
        cout << "B::f()" << endl;
    }
    int _b;
};

void Fun(A* p)
{
    B* pb = (B*)p;
    pb->f();
    cout << pb->_a << endl;
    cout << pb->_b << endl;
}
void Test4()
{
    A a;
    a._a = 1;
    B b;
    b._a = 2;
    b._b = 3;

    Fun(&a);
    Fun(&b);
}

运行结果如下:

8e53f9c5a7c35b2a1bf28ae315463b80.png


因为A类大小为8字节,B类为12字节,将一个A类指针强制转成B类,再用该指针去访问B类中的_b,会访问越界,读到的是随机值,一旦进行写,程序就会崩溃。
C++提供了一种更加安全的转换机制,使用dynamic_cast来进行转换。

注意: 1.dynamic_cast只能用于含有虚函数的类 2.dynamic_cast在转换时会先检查能否转换成功,能就进行转换,不能就返回0

将Fun函数进行改造:

void Fun(A* p)
{
    //B* pb = (B*)p;
    B* pb = dynamic_cast<B*>(p);
    if (pb)
    {
        cout << "可以转换 p是子类指针" << endl;
        pb->f();
        cout << pb->_a << endl;
        cout << pb->_b << endl;
    }
    else
    {
        cout << "不能转换 p是父类指针" << endl;
        p->f();
        cout << p->_a << endl;
    }
}

accad2840a0bec7f8a8c9ef0e80f1812.png


这样就可以保证我们代码的安全性,

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值