隐式类型转换 标准数据类型之间会进行隐式的类型安全转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <iostream> #include <string> using namespace std;int main () { short s = 'a' ; unsigned int ui = 1000 ; int i = -2000 ; double d = i; cout << "d = " << d << endl; cout << "ui = " << ui << endl; cout << "ui + i = " << ui + i << endl; if ( (ui + i) > 0 ) { cout << "Positive" << endl; } else { cout << "Negative" << endl; } cout << "sizeof(s + 'b') = " << sizeof (s + 'b' ) << endl; return 0 ; }
1 2 3 4 5 6 7 fengyun@ubuntu:~/share$ g++ test.cpp -o test fengyun@ubuntu:~/share$ ./test d = -2000 ui = 1000 ui + i = 4294966296 Positive sizeof(s + 'b' ) = 4
注意int 类型会隐式转化为unsigned int类型,17行的ui + i会将i的类型int隐式转换为unsigned int(编译器认为是安全的操作),这是一件非常危险的事情。(这种判断是经常有的,比如vector,string调用length()返回值是size_t即unsigned int,经常与我们自己定义的int类型做运算,那么很容易出现危险的事情)
注意26行sizeof(s + 'b')
理应是小字节类型向大字节类型转化,’b’(char)转化为short,而后两个short值相加,最终得到一个short,结果应该是2。这是我的理论分析。 然而编译器会对其进行优化,编译器将s 和’b’都转化为int,对大多数编译器看来,四个字节int类型的运算是最快的,那么编译器会将它们都隐式转化为int类型(编译器认为是安全的操作)。
C语言中强制类型转换 强制类型转换的格式为:
1 2 3 4 5 (type_name) expression (float ) a; (int )(x+y); (float ) 100 ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> typedef void (PF) (int ) ;struct Point { int x; int y; }; int main () { int v = 0x12345 ; PF* pf = (PF*)v; char c = char (v); Point* p = (Point*)v; pf(5 ); printf ("p->x = %d\n" , p->x); printf ("p->y = %d\n" , p->y); return 0 ; }
C方式强制类型转换存在的问题
过于粗暴,任意类型之间都可以进行转换,编译器很难判断其正确性
难于定位在源码中无法快速定位所有使用强制类型转换的语句
新式类型转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <stdio.h> void static_cast_demo () { int i = 0x12345 ; char c = 'c' ; int * pi = &i; char * pc = &c; c = static_cast <char >(i); pc = static_cast <char *>(pi); } void const_cast_demo () { const int & j = 1 ; int & k = const_cast <int &>(j); const int x = 2 ; int & y = const_cast <int &>(x); int z = const_cast <int >(x); k = 5 ; printf ("k = %d\n" , k); printf ("j = %d\n" , j); y = 8 ; printf ("x = %d\n" , x); printf ("y = %d\n" , y); printf ("&x = %p\n" , &x); printf ("&y = %p\n" , &y); } void reinterpret_cast_demo () { int i = 0 ; char c = 'c' ; int * pi = &i; char * pc = &c; pc = reinterpret_cast <char *>(pi); pi = reinterpret_cast <int *>(pc); pi = reinterpret_cast <int *>(i); c = reinterpret_cast <char >(i); } void dynamic_cast_demo () { int i = 0 ; int * pi = &i; char * pc = dynamic_cast <char *>(pi); } int main () { static_cast_demo (); const_cast_demo (); reinterpret_cast_demo (); dynamic_cast_demo (); return 0 ; }
C + +将强制类型转换分为4种不同的类型
用法:xxx_cast<Type>(Expression)
static_cast强制类型转换
用于基本类型 间的转换
不能 用于基本类型指针间 的转换
用于有继承关系类对象 之间的转换和类指针 之间的转换
const_cast强制类型转换
用于去除 变量的只读属性
强制转换的目标类型 必须是指针或引用
reinterpret_cast强制类型转换
用于指针类型间 的强制转换
用于整数 和指针类型 间的强制转换
dynamic_cast强制类型转换
用于有继承关系的类指针 间的转换
用于有交叉关系的类指针 间的转换
具有类型检查 的功能
需要虚函数的支持
dynamic_cast 是与继承相关的类型转换关键字 dynamic_cast 要求相关的类中必须有虚函数 用于有直接或者间接继承关系的指针( 引用 ) 之间
指 针 : 转换成功:得到目标类型的指针 转换失败:得到一个空指针
引 用 : 转换成功:得到目标类型的引用 转换失败:得到一个异常操作信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <iostream> #include <string> using namespace std;class Base { public : Base () { cout << "Base::Base()" << endl; } virtual ~Base () { cout << "Base::~Base()" << endl; } }; class Derived : public Base{ }; int main () { Base* p = new Base; Derived* pd = dynamic_cast <Derived*>(p); if ( pd != NULL ) { cout << "pd = " << pd << endl; } else { cout << "Cast error!" << endl;/ } delete p; return 0 ; }
普通类型转换为类类型
构造函数可以定义不同类型的参数
参数满足下列条件时称为转换构造函数有且仅有一个参数 参数是基本类型 参数是其它类类型 (参数不是当前自己类的类型即可)
1 2 3 4 5 6 7 8 9 int i;Test t; i = int (1.5 ); t = Test (100 ); t = 100 ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <iostream> #include <string> using namespace std;class Test { int mValue; public : Test (){ mValue = 0 ; } Test (int i){ mValue = i; } Test operator + (const Test& p) { Test ret (mValue + p.mValue) ; return ret; } int value () { return mValue; } }; int main () { Test t; t = 5 ; Test r; r = t + 10 ; cout << r.value () << endl; return 0 ; }
观察这段代码28行r = t + 10;
这里竟然能够编译通过并且最后的结果是15正确的。但是工程中谁会这样写代码呢?这应该被视作一个错误但是编译器展现了它的能力,这却容易给我们带来一个bug
1 2 3 fengyun@ubuntu:~/share$ g++ test.cpp -o test fengyun@ubuntu:~/share$ ./test 15
编译器尽力尝试的结果是隐式类型转换
隐式类型转换 会让程序以意想不到的方式进行工作 是工程中 bug 的重要来源
explicit
工程中通过 explicit 关键字杜绝编译器的转换尝试
转换构造函数被 explicit 修饰时只能进行显示转换
1 2 3 static_cast <ClassName>(value);ClassName (value);(ClassName)value;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <iostream> #include <string> using namespace std;class Test { int mValue; public : Test () { mValue = 0 ; } explicit Test (int i) { mValue = i; } Test operator + (const Test& p) { Test ret (mValue + p.mValue) ; return ret; } int value () { return mValue; } }; int main () { Test t; t = static_cast <Test>(5 ); Test r; r = t + static_cast <Test>(10 ); cout << r.value () << endl; return 0 ; }
小结
转换构造函数只有一个参数
转换构造函数的参数类型是其它类型
转换构造函数在类型转换时被调用
隐式类型转换是工程中 bug 的重要来源
explicit 关键字用于杜绝隐式类型转换
类类型转换到普通类型
1 2 3 4 fengyun@ubuntu:~/share$ g++ test.cpp -o test fengyun@ubuntu:~/share$ ./test t.value() = 100 i = 100
再看一个类与类之间的转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <iostream> #include <string> using namespace std;class Test ;class Value { public : Value () { } explicit Value (Test& t) { } }; class Test { int mValue; public : Test (int i = 0 ) { mValue = i; } int value () { return mValue; } operator Value () { Value ret; cout << "operator Value()" << endl; return ret; } }; int main () { Test t (100 ) ; Value v = t; return 0 ; }
Value v = t;
可以调用类型转换函数Value v = t.operator Value()
也可以调用转换构造函数Value v = Value(t)
。这会造成二义性而报错,因此我们在转换构造函数前加上关键字explicit
无法抑制隐式的类型转换函数调用
类型转换函数可能与转换构造函数冲突
工程中以 **Type toType()**的公有成员代替类型转换函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <QDebug> #include <QString> int main () { QString str = "" ; int i = 0 ; double d = 0 ; short s = 0 ; str = "-255" ; i = str.toInt (); d = str.toDouble (); s = str.toShort (); qDebug () << "i = " << i << endl; qDebug () << "d = " << d << endl; qDebug () << "s = " << s << endl; return 0 ; }
类型识别 在面向对象中可能出现下面的情况 - 基类指针指向子类对象 - 基类引用成为子类对象的別名
静态类型 - 变量( 对象 ) 自身的类型
动态类型 - 指针( 引用 ) 所指向对象的实际类型
1 2 3 4 5 void test (Base* b) { Derived* d = static_cast <Derived*>(b); }
基类指针是否可以强制类型转换为子类指针取决于动态类型 !
C+ +中如何得到动态类型?
解决方案 - 利用多态
在基类中定义虚函数 返回具体的类型信息
所有的派生类都必须实现 类型相关的虚函数
每个类中的类型虚函数都需要不同的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <iostream> #include <string> using namespace std;class Base { public : virtual string type () { return "Base" ; } }; class Derived : public Base{ public : string type () { return "Derived" ; } void printf () { cout << "I'm a Derived." << endl; } }; class Child : public Base{ public : string type () { return "Child" ; } }; void test (Base* b) { if ( b->type () == "Derived" ) { Derived* d = static_cast <Derived*>(b); d->printf (); } } int main (int argc, char *argv[]) { Base b; Derived d; Child c; test (&b); test (&d); test (&c); return 0 ; }
对类的实际类型判断之后,再使用static_cast转换 dynamic_cast可以判断转换是否成功,转换失败返回0,转换成功返回类的地址。
多态解决方案的缺陷
- 必须从基类开始 提供类型虚函数
- 所有的派生类都必须重写类型虚函数
- 每个派生类的类型名必须唯一
C+ +提供了 typeid 关键字用于获取类型信息
- typeid 关键字返回对应参数的类型信息
- typeid 返回一个 type_info 类对象
- 当 typeid 的参数为 NULL 时将拋出异常
1 2 3 4 int i = 0 ;const type_info& tiv = typeid (i);const type_info& tii = typeid (int );cout << (tiv == tii) << endl;
typeid 的注意事项
- 当参数为类型时 : 返回静态类型信息
- 当参数为变量时 : 不存在虚函数表- 返回静态类型信息 存在虚函数表- 返回动态类型倍息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <iostream> #include <string> #include <typeinfo> using namespace std;class Base { public : virtual ~Base (){ } }; class Derived : public Base{ public :}; void test (Base* b) { const type_info& tb = typeid (*b); cout << tb.name () << endl; } int main (int argc, char *argv[]) { int i = 0 ; const type_info& tiv = typeid (i); const type_info& tii = typeid (int ); cout << (tiv == tii) << endl; Base b; Derived d; test (&b); test (&d); return 0 ; }
1 2 3 4 5 fengyun@ubuntu:~/share$ g++ test.cpp -o test fengyun@ubuntu:~/share$ ./test 1 4Base 7Derived
typeid在不同编译器返回信息略有差别