类型转换
#include <iostream>
using namespace std;
int main()
{
double x=1.2;
int y;
cout<<(int)x<<endl; //函数的形式 类型转换
cout<<int(x)<<endl; //强制类型转换
}
据说,函数的方式是为了照顾从别的语言转到C语言的程序员的习惯!!!
这种形式的类型转换对于基本的数据类型是足够的,但是面对复杂的类型就显得捉襟见肘了,特别是对于C++中用于自定义的类类型。举例:
// class type-casting
#include <iostream>
using namespace std;
class Dummy {
double i,j;
};
class Addition {
int x,y;
public:
Addition (int a, int b) { x=a; y=b; }
int result() { return x+y;}
};
int main () {
Dummy d;
Addition * padd;
padd = (Addition*) &d;
cout << padd->result();
return 0;
}
这里将Dummy 类型的指针强制转化成Addition类型的指针,并赋值给padd。在VS2015中,这个程序编译是正确的,但是运行时要么出错要么会得到莫名其妙的结果。
为了控制C++ class之间的类型转换,C++提供了四种class类型转换机制,分别具有如下形式:
dynamic_cast <new_type> (expression)
reinterpret_cast <new_type> (expression)
static_cast <new_type> (expression)
const_cast <new_type> (expression)
下面逐一来看一下这些class类型的转换机制具有什么特点。
dynamic_mic方法
dynamic_cast 只能用于class的指针或者引用之间的类型转换。dynamic_cast 保证转换后所得的指针指向的是一个完整的类实例。
也就是说,对于下面这种类型的转换:
pointer1 = dynamic_cast <pointer2> (expression)
pointer1所指向的类实例中的所有成员都可以在pointer2的类实例中找到。以下面为例子:
#include <iostream>
#include <exception>
using namespace std;
class Base { virtual void dummy() {} };
class Derived: public Base { int a; };
int main () {
try {
Base * pba = new Derived; //指向派生类实例的基类指针
Base * pbb = new Base; //指向基类实例的基类指针
Derived * pd;
pd = dynamic_cast<Derived*>(pba);
if (pd==0) cout << "Null pointer on first type-cast.\n";
pd = dynamic_cast<Derived*>(pbb);
if (pd==0) cout << "Null pointer on second type-cast.\n";
} catch (exception& e) {cout << "Exception: " << e.what();}
return 0;
}
这个例子的输出是
Null pointer on second type-cast.
之所以第二个类型转换失败是因为pd 指向的类实例的所有成员不能在pbb指向的类实例中找到。根据声明可知,pd 指向派生类,pbb指向基类,显然基类是派生类的子集,派生类中的所有成员不可能都在基类中找得到。
那为什么第一个类型转换成功呢?我们看到
Base * pba = new Derived;
pba虽然声明成的是一个基类指针,但是它实际上指向的是一个派生类的实例。自然同一个类的所有成员都可以在同一个类中找到。
按照上面的套路好像dynamic_cast只适合于将指向派生类的指针转换为指向基类的指针(upcast ),但是事实并非如此。
事实上,dynamic_cast也能够将基类指针转换成派生类的指针。但是必须满足一个条件:这个基类指针所指向的必须是一个派生类,其实就是上面例子所说的。
综上,我们可以看到dynamic_cast转换提供了一个检查机制,它保证转换得到的指针所指向的内存是有效的。亦即按照该指针的声明类型进行内存操作不会产生意想不到的结果。
static_cast
static_cast can perform conversions between pointers to related
classes
static_cast就比较随意了。它不提供检查机制。我们可以随意将指向基类的指针转换为指向派生类的指针,或者指向派生类的指针转化为指向基类的指针。比如对于下面的例子
class Base {};
class Derived: public Base {};
Base * a = new Base;
Derived * b = static_cast<Derived*>(a);
它不会像dynamic_cast那样保证转换后b指向的实例中的所有数据成员都有效。这点只能由程序员自己去做类型检查。
static_cast is also able to perform all conversions allowed implicitly (not only those with pointers to classes), and is also able to perform the opposite of these.
static_cast还支持所有其他的隐式类型转换。比如将void指针转换为其他类型指针,将整型,浮点型转换为枚举类型等等。
reinterpret_cast
reinterpret_cast converts any pointer type to any other pointer type,
even of unrelated classes.
和static_cast类型转换机制相比,reinterpret_cast更随意了。它甚至允许不相关(不在一个继承体系内的类)类指针进行相互转换。比如这样
class A { /* ... */ };
class B { /* ... */ };
A * a = new A;
B * b = reinterpret_cast<B*>(a);
除此之外,我们还可以用reinterpret_cast将指针类型转换成整型数,或者将整型数转换成指针类型。当然,转换成的整型数的形式则是机器相关的。
reinterpret_cast可以简单理解为对一个二进制序列(指针或者其他类型变量的内存表示形式)的重新解释,比如将具有指针意义的二进制序列解释为具有整数意义的二进制序列,将指向类A的指针的二进制形式解释为指向类B的二进制形式。所以reinterpret_cast是极度机器相关的。很多时候不同机器得到的转换结果不一样。
const_cast
This type of casting manipulates the constness of the object pointed by a pointer, either to be set or to be removed. For example, in order to pass a const pointer to a function that expects a non-const argument:
最后这个比较简单,就是将const变量转换成非const型的变量;或者将非const变量转换成const变量。
#include <iostream>
using namespace std;
void print (char * str)
{
cout << str << '\n';
}
int main () {
const char * c = "sample text";
print ( const_cast<char *> (c) );
return 0;
}
当然这里的安全性也是要有程序员负责,因为将const变量转换成非const可能会使用户改变这个本不该改变的变量,使程序出现问题。
string类型转换
方法1:**重点内容**stringstream
可以将string类型转换为整型、char数组类型
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
string text="123";
int int_n1,int_n2;
char c[4]; //保证字符长度大于(字符串长度+1)
cout<<text.length()<<endl;
stringstream ss;
ss<<text; //可以是其他数据类型
ss>>int_n1;
ss.clear(); //多次转换的时候 最好使用同一个ss(类的构造费时间) 但一定要用clear方法!!!
ss<<text;
ss>>int_n2;
ss.clear();
ss<<text();
ss>>c;
cout<<int_n1<<" "<<int_n2<<endl;
}
方法2:string类提供的方法
标准库的string类提供了3个成员函数来从一个string得到c类型的字符数组:c_str()、data()、copy(p,n)
。
1. c_str():生成一个const char*指针,指向以空字符终止的数组。
注:
①这个数组的数据是临时的,当有一个改变这些数据的成员函数被调用后,其中的数据就会失效。因此要么现用先转换,要么把它的数据复制到用户自己可以管理的内存中。注意。看下例:
const char* c;
string s="1234";
c = s.c_str();
cout<<c<<endl; //输出:1234
s="abcd";
cout<<c<<endl; //输出:abcd
上面如果继续用c指针的话,导致的错误将是不可想象的。就如:1234变为abcd
其实上面的c = s.c_str(); 不是一个好习惯。既然c指针指向的内容容易失效,我们就应该按照上面的方法,那怎么把数据复制出来呢?这就要用到strcpy等函数(推荐)。
代码实现如下:
#include <iostream>
#include <string>
#include <exception>
using namespace std;
int main ()
{
char a[5]={'1','2','3','4',0};
string s="2468";
char b[5]; //b数组长度>=a数组长度 b数组长度>=s字符串长度+1
strcpy(b,a); //字符串数组copy到另一个字符串数组
cout<<b<<endl;
string s1(b,b+2); //字符串数组->string 并且可以指定位置 前闭后开
cout<<s1<<endl;
strcpy(b,s.c_str()); //string->字符串数组
cout<<b<<endl;
string s2(s1.substr(0,2)); //string ->string 指定位置
cout<<s2<<endl;
string s3(s2.c_str()); //s2转换为字符数组 再赋值给string
cout<<s3<<endl;
string s4(s3); //直接初始化
cout<<s4<<endl;
return 0;
}
② c_str()返回一个客户程序可读不可改的指向字符数组的指针,不需要手动释放或删除这个指针。
2、data():与c_str()类似,但是返回的数组不以空字符终止。
3、copy(p,n,size_type _Off = 0):从string类型对象中至多复制n个字符到字符指针p指向的空间中。默认从首字符开始,但是也可以指定,开始的位置(记住从0开始)。返回真正从对象中复制的字符。——用户要确保p指向的空间足够保存n个字符。