C++ 类型转换

隐式类型转换:

  从构造函数形参类型 到 该类类型 的一个编译器的自动转换。

#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;

class Person {
private:
	string _name;
public:
	bool compareName(const Person& other) {
		return other._name == _name;
	}

	Person(string name):_name(name) {}
};
int main()
{
	Person A("Lilei");
	Person B("HanMeiMei");

	cout << A.compareName(B) << endl;//1 正常进行比较,无需发生转换

	cout << A.compareName(string("Lilei")) << endl;// 2 此处发生了一个隐式转换:string 类型-》Pereson类型,借助了Person的构造函数,为了满足compareName的参数要求

	cout << A.compareName(Person("Lilei")) << endl;//3 显式创建临时对象
	system("pause");
	return 0;
}

           代码中可以看到,compareName函数期待一个Persion类型形参的,但我们却传递了一个string类型的给它,这是因为,Person类中有个构造函数,它使用一个string类型实参进行调用,编译器调用了这个构造函数,隐式地将stirng类型转换为Person类型(构造了一个Person临时对象),再传递给compareName函数。

       隐式类型转换是会带来风险的,如上面标记,隐式转换得到类的临时变量,完成操作后就消失了,我们构造了一个完成测试后被丢弃的对象。

  我们可以通过explicit声明来抑制这种转换:

explicit  Person(string name):_name(name) {}

 explicit关键字只能用于类内部的构造函数声明上.这样一来,Person类构造函数就不能用于隐式地创造对象了,编译上面的代码会出现这样的提示:


现在用户只能进行显示类型转换,显式地创建临时对象

 

显式(强制)类型转换

1) static_cast

用法:static_cast <类型说明符> (变量或表达式)

  • 用于类层次结构中基类和派生类之间指针或引用的转换

      进行上行转换(把派生类的指针或引用转换成基类表示)是安全的
      进行下行转换(把基类的指针或引用转换为派生类表示),由于没有动态类型检查,所以是不安全的

  •     用于基本数据类型之间的转换,如把int转换成char。这种转换的安全也要开发人员来保证
  •     把空指针转换成目标类型的空指针
  •     把任何类型的表达式转换为void类型

如:基本类型转换

int a = 10;
int b = 3;

double result = static_cast<double>(a) / static_cast<double>(b);
cout << result << endl;

如:转换基类 派生类指针

// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;


class Person {
};

class XiaoMing :public Person {};

int main()
{
	Person b;
	Person* pb;

	XiaoMing d;
	XiaoMing* pd;

	pb = static_cast<Person*>(&d);//派生类 转 基类 ,安全
	pd = static_cast<XiaoMing*>(&b);//基类 转 派生类,不安全

	cout << "指向基类对象的基类指针 转换为派生类指针" << endl;
	cout << pd << endl;
	cout << &b << endl;

	cout << "指向派生类对象的基类指针 转换为派生类指针" << endl;
	pb = new XiaoMing;
	pd = static_cast<XiaoMing*>(pb);
	cout << pb << endl;
	cout << pd << endl;

	system("pause");
	return 0;
}

如:转换两个完全不相关的指针

// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;


class Person {
};

class XiaoMing {
};

int main()
{
	Person b;
	Person* pb;

	XiaoMing d;
	XiaoMing* pd;

	pb = static_cast<Person*>(&d);//派生类 转 基类 ,安全
	pd = static_cast<XiaoMing*>(&b);//基类 转 派生类,不安全

	cout << "指向基类对象的基类指针 转换为派生类指针" << endl;
	cout << pd << endl;
	cout << &b << endl;

	cout << "指向派生类对象的基类指针 转换为派生类指针" << endl;
	pb = new XiaoMing;
	pd = static_cast<XiaoMing*>(pb);
	cout << pb << endl;
	cout << pd << endl;

	system("pause");
	return 0;
}

编译失败

2)dynamic_cast 

用法:dynamic_cast<type_id> (expression)

  • 其他三种都是编译时完成的,dynamic_cast是运行时处理的,运行时要进行类型检查。
  • 不能用于内置的基本数据类型的强制转换。
  • dynamic_cast转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL。
  • 使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过。

        B中需要检测有虚函数的原因:类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义。

        这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,

        只有定义了虚函数的类才有虚函数表。

  •  在类的转换时,在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

        向上转换,即为子类指针指向父类指针(一般不会出问题);向下转换,即将父类指针转化子类指针。

       向下转换的成功与否还与将要转换的类型有关,即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败。

  •  在C++中,编译期的类型转换有可能会在运行时出现错误,特别是涉及到类对象的指针或引用操作时,更容易产生错误。Dynamic_cast操作符则可以在运行期对可能产生问题的类型转换进行测试。
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;


class Person {
public:
	void m() {
		cout << "m" << endl;
	}
};

class XiaoMing:public Person {
public:
	void f() { cout << "f" << endl; }
};

int main()
{
	XiaoMing* p;
	p = new Person;
	p = static_cast<XiaoMing*>(new  Person);
	p->m();
	p->f();

	system("pause");
	return 0;
}

本例中定义了两个类:Person类和XiaoMing类,这两个类构成继承关系。在Person类中定义了m函数,XiaoMing类中定义了f函数。在前面介绍多态时,我们一直是用基类指针指向派生类或基类对象,而本例则不同了。本例主函数中定义的是一个派生类指针,当我们将其指向一个基类对象时,这是错误的,会导致编译错误

但是通过强制类型转换我们可以将派生类指针指向一个基类对象,p = static_cast<derived *>(new Person);语句实现的就是这样一个功能,这样的一种强制类型转换时合乎C++语法规定的,但是是非常不明智的,它会带来一定的危险。

在程序中p是一个派生类对象,我们将其强制指向一个基类对象,首先通过p指针调用m函数,因为基类中包含有m函数,这一句没有问题,之后通过p指针调用f函数。一般来讲,因为p指针是一个派生类类型的指针,而派生类中拥有f函数,因此p->f();这一语句不会有问题,但是本例中p指针指向的确是基类的对象,而基类中并没有声明f函数,虽然p->f();这一语句虽然仍没有语法错误,但是它却产生了一个运行时的错误。换言之,p指针是派生类指针,这表明程序设计人员可以通过p指针调用派生类的成员函数f,但是在实际的程序设计过程中却误将p指针指向了一个基类对象,这就导致了一个运行期错误。

产生这种运行期的错误原因在于static_cast强制类型转换时并不具有保证类型安全的功能,而C++提供的dynamic_cast却能解决这一问题,dynamic_cast可以在程序运行时检测类型转换是否类型安全。

当然dynamic_cast使用起来也是有条件的,它要求所转换的操作数必须包含多态类类型(即至少包含一个虚函数的类)。

// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;


class Person {
public:
	void m() {
		cout << "m" << endl;
	}
};

class XiaoMing:public Person {
public:
	void f() { cout << "f" << endl; }
};

int main()
{
	XiaoMing* p;
	//p = new Person;
	p = dynamic_cast<XiaoMing*>(new  Person);
	p->m();
	p->f();

	system("pause");
	return 0;
}

 

在本例中利用dynamic_cast进行强制类型转换,但是因为Person类中并不存在虚函数,因此p = dynamic_cast<XiaoMing*>(new  Person);;这一句会编译错误。

为了解决本例中的语法错误,我们可以将Person类中的函数m声明为虚函数,virtual void m(){cout<<"m"<<endl;}。

dynamic_cast还要求<>内部所描述的目标类型必须为指针或引用。

// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;


class Person {
public:
	void virtual m() {
		cout << "m" << endl;
	}
};

class XiaoMing:public Person {
public:
	void m() { cout << "m2" << endl; }
};

int main()
{
	Person* a1 = new XiaoMing;//a1 是Person类型的指针 指向一个XiaoMing类型的对象
	Person* a2 = new Person;//a2 是Person类型的指针 指向了一个Person类型的对象

	XiaoMing* b;

	b = dynamic_cast<XiaoMing*>(a1);//结果 为 not null,向下转换成功
	if (b == NULL)
		cout << "null" << endl;
	else
	{
		cout << "not null" << endl;
	}

	b = dynamic_cast<XiaoMing*>(a2);//结果为null,向下转化失败
	if (b == NULL)
		cout << "null" << endl;
	else
		cout << "not null" << endl;

	system("pause");
	return 0;
}

3)const_cast

只用使用const_cast才能将const性质转换掉。在这种情况下,试图使用其他三种形式的强制转换都会导致编译时的错误。类似地,除了添加或者删除const特性,用const_cast符来执行其他任何类型转换,都会引起编译错误。

const double val=3.14;
double *ptr=NULL;
  
/*为了使ptr指向val,使用const_cast*/
ptr=const_cast<double *>(&val);

在《C++ primer》(第五版)中是这样介绍const_cast的:

const_cast只能改变运算对象的底层const

const char *pc;
char *p=const_cast<char*>(pc);//正确但是通过p写值是未定义的行为

对于将常量对象转换成非常量对象的行为,我们一般称其为“去掉const性质(cast away the const)”。一旦我们去掉了某个对象的const性质,编译器就不再阻止我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。然而如果对象是一个常量,再使用const_cast执行写操作就会产生未定义的后果。

只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量属性都将引发编译器错误。同样的,也不能用const_cast改变表达式的 类型:

const char* cp;
//错误:static_cast不能转换const的性质
char *q=static_cast<char*>(cp);
static_cast<string>(cp);//正确:字符串字面值转换为string类型
const_cast<string>(cp);//const_cast只改变常量属性

 4)reinterpret_cast

从语法上看,这个操作符仅用于指针类型的转换(返回值是指针)。它用来将一个类型指针转换为另一个类型指针,它只需在编译时重新解释指针的类型。这个操作符基本不考虑转换类型之间是否是相关的

int *ip=NULL;
char *pc=reinterpret_cast<char *>(ip);
  
/*注:必须牢记pc所指的真实对象是一个int而非字符,如果把pc当成普通的字符指针使用
*就可能在运行时发生错误*/

 

参考:https://www.cnblogs.com/QG-whz/p/4472566.html

https://www.cnblogs.com/Allen-rg/p/6999360.html

https://blog.csdn.net/znzxc/article/details/81177792

https://www.cnblogs.com/wuyepeng/p/9783893.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

土拨鼠不是老鼠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值