类型转换

隐式转换

当值被复制到一个兼容类型的隐式转换自动执行。例如:

1
2
3
short a=2000;
int b;
b=a;
 


这里,值 a是从提升 shortint,而不需要任何显式操作者。这被称为一个 标准转换。标准转换的影响基本数据类型,并允许数值类型之间的转换( shortintintfloatdoubleint...),或从 bool,有的指针转换。

转换为 int一些更小的整数类型,或者 doublefloat被称为 促进,并保证以产生在目标类型的值完全相同。算术类型之间的转换等并不总是能够准确地表示相同的值:
  • 如果一个负整数值被转换为一个无符号的类型,将所得的值对应于它的2的补按位表示(即,-1变成由类型,最大值可表示-2第二大的,...)。
  • 从/转换来bool考虑false等同于(对于数值类型)和空指针(指针类型); true相当于所有其他值,并转换成的等效1
  • 如果转换是从一个浮点类型为整数类型,值被截断(小数部分被去除)。如果结果位于由式表示的值的范围之外,则转换导致未定义的行为
  • 否则,如果转换是数字类型相同类型的(整数到整数或浮点到浮动)之间,该转换是有效的,但该值是特定于实现的(并且可能无法移植)。

有些转换可能意味着精度损失,该编译器可以用一个警告信号。可以用显式转换来避免此警告。

对于非基本类型,数组和函数隐式转换的指针,并在一般指针允许下列转化:
  • 空指针可以转换为任何类型的指针
  • 指针的任何类型的可转换为void指针。
  • 指针向上转型:指针派生类可转换为一个的指针访问明确的基类,而无需修改其constvolatile资格。

隐式转换带班

在类的世界中,隐式转换可以通过三个成员函数来控制:
  • 单参数的构造函数:允许隐式转换,从一个特定类型的初始化对象。
  • 赋值运算符:允许从分配特定类型的隐式转换。
  • 类型转换操作符:允许隐式转换为特定类型。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// implicit conversion of classes:
#include <iostream>
using namespace std;

class A {};

class B {
public:
  // conversion from A (constructor):
  B (const A& x) {}
  // conversion from A (assignment):
  B& operator= (const A& x) {return *this;}
  // conversion to A (type-cast operator)
  operator A() {return A();}
};

int main ()
{
  A foo;
  B bar = foo;    // calls constructor
  bar = foo;      // calls assignment
  foo = bar;      // calls type-cast operator
  return 0;
}
 


该类型转换操作符使用一个特定的语法:它使用 operator关键字后跟目标类型和一组空括号。注意,返回类型为目标类型,因而前未指定 operator关键字。

明确的关键字

在一个函数调用,C ++允许一个隐式转换发生的每一个参数。这可以是用于类有点问题,因为它并不总是意。例如,如果我们添加的功能到最后的示例:

 
void fn (B arg) {}
 


此函数采用类型的参数 B,但它可能也与类型的对象调用 A的参数:

 
fn (foo);
 


这可能或可能不想要的结果。但是,在任何情况下,它可以通过标记与受影响的构造防止 explicit关键字:

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
// explicit:
#include <iostream>
using namespace std;

class A {};

class B {
public:
  explicit B (const A& x) {}
  B& operator= (const A& x) {return *this;}
  operator A() {return A();}
};

void fn (B x) {}

int main ()
{
  A foo;
  B bar (foo);
  bar = foo;
  foo = bar;
  
//  fn (foo);  // not allowed for explicit ctor.
  fn (bar);  

  return 0;
}


此外,构造标记 explicit不能与分配的语法被调用; 在上述例子中, bar已经不能构造有:

 
B bar = foo;
 


型铸成员函数(那些在上一节中描述的)也可以指定为 explicit。这可以防止隐式转换以同样的方式作为 explicit-specified构造为目标类型做。

铸造型

C ++是一种强类型语言。许多转换,特别是那些暗示的价值有不同的解释,需要一个明确的转换,在C ++著名的 类型转换。存在于通用型铸造两大语法: 功能类似C

1
2
3
4
double x = 10.3;
int y;
y = int (x);    // functional notation
y = (int) x;    // c-like cast notation 
 


的类型转换,这些一般形式的功能足以满足大多数需求与基本的数据类型。然而,这些操作符可以被不加区别地对类和指针应用于类,这可能会导致该助益,而在被语法正确─可导致运行错误代码。例如,下面的代码编译没有错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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;
}
 


程序中声明了一个指针 Addition,但随后将其分配给它使用显式类型转换另一个不相关类型的对象的引用:

 
padd = (Addition*) &d;
 


无限制明确的类型转换允许任何指针转换为任何其他指针类型,独立它们指向的类型。对会员的后续调用 result将产生无论是运行时错误或其他一些意想不到的结果。

为了控制这些类型的类之间的转换,我们有四个具体的转换操作符: dynamic_castreinterpret_caststatic_castconst_cast。它们的格式是按照新型尖括号(间封闭的 <>),紧接着,以括号之间转换的表达。 传统型的铸造等同这些表达式将是: 但每个人有自己的特色:

dynamic_cast <new_type> (expression)
reinterpret_cast <new_type> (expression)
static_cast <new_type> (expression)
const_cast <new_type> (expression)



(new_type) expression
new_type (expression)



的dynamic_cast

dynamic_cast只能与指针和引用类(或使用 void*)。其目的是确保类型转换点到目的地的指针类型的有效的完整对象的结果。

这自然包括 指针向上转型(从指针到衍生指针到基变换),以同样的方式作为允许作为一个 隐式转换

dynamic_cast垂头丧气(从指针到基转换为指针到派生)多态类(那些虚拟成员),如果只-and如-尖锐的物体是目标类型的有效完整的对象。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// dynamic_cast
#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指针。


兼容性注意:这种类型的dynamic_cast需要运行时类型信息(RTTI)来跟踪动态类型。一些编译器支持此功能作为默认是关闭的选项。这需要对运行时类型检查使用要启用dynamic_cast与这些类型的正常工作。

上面的代码试图执行从类型的指针的对象两个动态管型 Base*pbapbb)对类型的指针对象 Derived*,但是只有第一个是成功的。注意各自的初始化:

1
2
Base * pba = new Derived;
Base * pbb = new Base;
 


即使两者都是类型的指针 Base*pba实际上指向类型的对象 Derived,而 pbb指向类型的对象 Base。因此,当使用执行它们各自的类型强制转换 dynamic_castpba指向类的完整的对象 Derived,而 pbb指向类的一个对象 Base,它是类的一个不完整的对象 Derived

dynamic_cast无法施展一个指针,因为它不是在前面的示例-第二次转换所需的类-as完整的对象时,它返回一个 空指针来指示失败。如果 dynamic_cast用于转换到参考类型和转换是不可能的,类型的异常 bad_cast代替抛出。

dynamic_cast还可以执行允许在指针其它隐式转换:转换指针类型之间空指针(甚至不相关的类之间),和铸造的任何类型的任何指针一个 void*指针。

的static_cast

static_cast可以执行指针之间的转换相关的类,不仅 upcasts(从指针到衍生指针至基站),但也 向下转换(从指针到基地指针到衍生的)。没有检查运行时进行,以保证所转换的对象实际上是在目标类型的完整的对象。因此,它是由程序员,以确保在转换是安全的。在另一边,它不会招致的类型安全检查的开销 dynamic_cast

1
2
3
4
class Base {};
class Derived: public Base {};
Base * a = new Base;
Derived * b = static_cast<Derived*>(a);
 


这将是有效的代码,虽然 b将指向类的一个不完整的对象,如果解除引用可能导致运行时错误。

因此, static_cast能够与指针班不仅让含蓄的转换来执行,而且他们的相反转换。

static_cast还能够执行允许隐含地(不仅是那些与指针的类)的所有转换,并且还能够执行的这些相反。它可以:
  • 从转换void*到任何指针类型。在这种情况下,它保证了,如果void*是由从同一指针类型转换获得的值,将得到的指针值是相同的。
  • 将整数,浮点值和枚举类型枚举类型。

此外, static_cast还可以执行以下步骤:
  • 显式调用单参数构造函数或转换操作符。
  • 转换为右值引用
  • 转换enum class价值为整数或浮点数。
  • 转换任何类型void,评估和丢弃的值。

reinterpret_cast的

reinterpret_cast转换甚至不相关的类任何指针类型到任何其他指针类型。运算结果是值的简单的二进制副本从一个指针到其他。所有的指针转换允许的:无论是内容指出,也不是指针类型本身被选中。

它也可以投三分球,或从整数类型。在此整数值表示指针的格式是特定于平台的。唯一的保证是一个指针转换为整数类型足够大,可以完全包含它(如 intptr_t),是保证能够被转换回一个有效的指针。

可以由要执行的转换 reinterpret_cast而不是由 static_cast是基于重新解释的种类,这在代码大多数情况下的结果是系统特定的,并且因此非便携式的二进制表示低级操作。例如:

1
2
3
4
class A { /* ... */ };
class B { /* ... */ };
A * a = new A;
B * b = reinterpret_cast<B*>(a);
 


此代码编译,虽然它并没有太大的意义,因为现在 b指向一个完全无关,并可能不兼容的类的对象。解引用 b是不安全的。

const_cast会

这种类型的铸造的操纵由指针指向对象的常量性,无论是被设置或除去。例如,为了一个const指针传递给需要非const参数的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// const_cast
#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;
}
示例文本


上面的例子是有保障的工作,因为功能 print不写入尖锐的物体。但请注意,这移除指向对象的常量性实际上写它会导致 不确定的行为

typeid的

typeid允许检查一个表达式的类型: 这个操作符返回类型的常量对象的引用是在标准头文件中定义。通过返回的值可以通过返回另一个值进行比较,使用运算符和,也可以通过服务使用其获得代表的数据类型或类名的空终止字符序列成员。

typeid (expression)

type_info <typeinfo> typeid typeid == != name()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// typeid
#include <iostream>
#include <typeinfo>
using namespace std;

int main () {
  int * a,b;
  a=0; b=0;
  if (typeid(a) != typeid(b))
  {
    cout << "a and b are of different types:\n";
    cout << "a is: " << typeid(a).name() << '\n';
    cout << "b is: " << typeid(b).name() << '\n';
  }
  return 0;
}
a和b是不同的类型:
一个是:INT *
b为:INT  


typeid被施加到类, typeid使用RTTI跟踪动态对象的类型。当 typeid被施加到其类型是一个多态类的表达,其结果是最派生完整的对象的类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// typeid, polymorphic class
#include <iostream>
#include <typeinfo>
#include <exception>
using namespace std;

class Base { virtual void f(){} };
class Derived : public Base {};

int main () {
  try {
    Base* a = new Base;
    Base* b = new Derived;
    cout << "a is: " << typeid(a).name() << '\n';
    cout << "b is: " << typeid(b).name() << '\n';
    cout << "*a is: " << typeid(*a).name() << '\n';
    cout << "*b is: " << typeid(*b).name() << '\n';
  } catch (exception& e) { cout << "Exception: " << e.what() << '\n'; }
  return 0;
}
一个是:Base类*
b为:Base类*
*一个是:Base类
* b为:类派生


注:成员返回的字符串nametype_info依赖于具体的实现你的编译器和库。它不一定是一个简单的字符串及其典型的类型名称,像在用于生产本输出的编译器。 

请注意,该类型如何 typeid考虑的指针是指针类型本身(包括 ab有型的 class Base *)。然而,当 typeid被应用到对象(如 *a*btypeid产生它们的动态类型(例如其大部分衍生完整的对象的类型)。

如果类型 typeid评估是由引用操作(开头的指针 *),而这个指针具有空值, typeid抛出一个 bad_typeid异常。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值