Explicit 关键字和各类类型转换(转)

说实话,历来没有感受到这个关键字有用,直到今天。java

explicit的意思是明显的,和它相对应的一个词是implicit意思是隐藏的。linux

我参考了MSDN和《c++标准程序库》对这个关键字的描述,并参考了网络上对这个关键字的解释。现将它的使用方法和总结记录以下:ios

首先这个关键字只能用在类构造函数。它的做用是不能进行隐式转换c++

class gxgExplicit  //没有关键字explicit的类程序员

{express

public:windows

   int _size;安全

   gxgExplicit(int size)网络

   {函数

      _size = size;

   }

};

下面是调用

   gxgExplicit gE1(24);     //这样是没有问题的

   gxgExplicit gE2 = 1;     //这样也是没有问题的

   gxgExplicit gE3;         //这样是不行的,没有默认构造函数

   gE1 = 2;                 //这样也是没有问题的

   gE2 = 3;                 //这样也是没有问题的

   gE2 = gE1;               //这样也是没有问题的

 

可是假如gxgExplicit修改成Stack,咱们的_size表明的是堆栈的大小,那么调用的第二句就显得不三不四,并且容易让人疑惑。这并非可让代码阅读者明白和接受的形式,虽然它是合法的(编译器能够经过编译)。这是由于编译器默认状况下有隐式转换的功能,你输入gE2 = 1就编译成同第一句相同的结果。因此,explicit就派上了用场。修改代码为:

class gxgExplicit

{

public:

   int _size;

   explicit gxgExplicit(int size)

   {

      _size = size;

   }

};

继续上面的调用:

   gxgExplicit gE1(24);     //这样是没有问题的

   gxgExplicit gE2 = 1;     //这样是不行的,关键字取消了隐式转换

   gxgExplicit gE3;         //这样是不行的,没有默认构造函数

   gE1 = 2;                 //这样是不行的,关键字取消了隐式转换

   gE2 = 3;                 //这样是不行的,关键字取消了隐式转换

   gE2 = gE1;               //这样是不行的,关键字取消了隐式转换,除非类实现操做符“=”的重载。

这是编译器(vs2005)显示:cannot convert from 'int' to 'gxgExplicit'

从这里也就看出这个关键字的做用是将编译器隐式转换的功能给屏蔽掉

 

MSDN上有一个注意点描述了下面的事实,当构造函数参数超过两个时自动取消隐式转换。例如

class gxgExplicit

{

private:

   int _size;

   int _age;

public:

   explicit gxgExplicit(int age, int size)

   {

      _age = age;

      _size = size;

   }

};

这是有没有关键字效果是同样的。那就是至关于有这个关键字

可是另一种状况例外:其中只有一个必须输入的参数,其他的为有默认值的参数。

class gxgExplicit

{

private:

   int _size;

   int _age;

public:

   explicit gxgExplicit(int age, int size = 0)

   {

      _age = age;

      _size = size;

   }

};

class gxgExplicit

{

private:

   int _size;

   int _age;

int _hight;

public:

   explicit gxgExplicit(int age, int size = 0)

   {

      _age = age;

      _size = size;

      _hight = hight;

   }

};

 

这样的状况下至关于一个参数的效果。

到如今为止。这个关键字就是这么用了。

 

 

呵呵,今天来好好看看着几个转换操做符的用法。之前总是看着眼熟,可是用着手生。今天决定搞定这些个东西。

在C语言中类型转换有几种方式:

1.      (expression). 在表达式外边加括号,由编译器来决定怎么改变。

2.      new_type(expression). 强制类型括号住表达式。

3.      (new_type)expression. 括号住强制类型。

4.      C语言容许的内置转换。

这些转换很是高效,我很是喜欢使用。特别是在指针转换和数值转换时用到的很是多。只要编写程序的人知道本身要作什么转换,并知道应该怎样转换的话,我认为上边的转换方式很是之好。可是没有清楚的了解每一个转换的细节的话,就有可能出现问题,好比指针指向不该该指向的区域:出现野指针或者指向位置错误(主要是对内存结构不了解),或者计算数值被截去等问题发生。

C++程序兼容C语言的转化,可是针对面向对象语言的特性,设计了如下几个类型转换操做符。他们的出现是为了C语言类型转换中语义模糊和固有的危险陷阱,由于C语言不去判断所要操做的类型转换是否合理。

static_cast:用于非多态类型的转换。

dynamic_cast:用于多态类型的转换。

const_cast:用来消除const, volatile, __unaligned属性的转换。

reinterpret_cast:用于空间的从新解释。

还有一个在VS2005中出现的类型转换关键字safe_cast.#2

 

static_cast:

static_cast<type_id>(expression)

这个关键字能够用来将一个指针转换为父类的指针也能够转换为子类的指针或者基本的类型转换。可是这种转换是强制的,并无任何运行时类型检查来保证转换的正确性,因此编写代码的人须要明白本身所进行的转换是否合理。

//基本类型的转换

enum e { A = 1, B, C };

double d = 12.25;

unsigned int ui = 25;

char c = static_cast<char>(ui);

int i = static_cast<int>(d);

int j = static_cast<int>(B);

//父类子类转换

class F                  //father

{

public:

    int _father;

};

class S : public F       //son

{

public:

    _son;

};

F *pFather = new F();

S *pSon = new S();

F *pF;

S *pS;

pF = static_cast<F *>(pSon);    //将子类指针转换为父类指针,OK

pS = static_cast<S *>(pFather); //将父类指针转换为子类指针,错误

第二个错误的转换不是说编译器编译不过去,而是运行时会出现错误。

缘由以下:假设pF指向了pSon的位置,它能够访问_father,它找不到_son,这样没有问题。可是pS指向了pFather的位置,它访问_father没有问题,可是访问_son时就会产生错误,由于pFather根本没有_son这个变量。

下面是将父类转换为子类指针时,static_cast和dymanic_cast二者不一样的表现:

class F

{

public:

    virtual void speak(){};

    int i;

};

 

class S : public F

{

public:

    void speak()

    {

       cout << "S = " << _s << endl;

    }

    double _s;

};

F *pF = new F();

S *pS = static_cast<S*>(pF);

pS->speak();

S1 *pDS = dynamic_cast<S*>(pF);

pDS->speak();

静态的转换编译不显示警告,运行结果不输出(调用F的speak函数)。动态的转换编译显示可能出现不可预期的结果,运行时崩溃。(VS2005时,返回空指针,可是不会崩溃。我认为要是按照C++的特性仍是崩溃比较好一点,让程序员容易理解这么作是错误的。)

 

dynamic_cast:

dynamic_cast<type_id>(expression)

本关键字主要处理多态的类型转换,type_id要么是指针类型,要么是引用类型要么是void*。当type_id为指针和void*时,expression必须是type_id类型的指针,当type_id为引用时,expression也必须是type_id类型的引用。#1

 

1.最经常使用的用法就是将子类指针转换为父类指针。(不举例)

2.当type_id为void*时,指针指向整个对象的空间。

Class A;

A *pA = new A();

void *p = dynamic_cast<void*>(pA);

可是type_id不为void*时,计算机就要在运行时检查是否可以转换。

3.跳级转换。

class A{};

class B : public A{};

class C : public B{};

    A *pA;

    B *pB;

    C *pC = new C();

    pB = dynamic_cast<B*>(pD);  //逐级转换OK

    pA = dynamic_cast<A*>(pB);  //逐级转换OK

    或者

    pA = dynamic_cast<A*>(pC);  //跳级转换OK

    delete pD;

如下状况跳级转换不能够:


class A{};

class B : public A{};

class C : public A{};

class D : public B, public C{};

    A *pA;

    D *pD = new D();

    pA = dynamic_cast<A*>(pB);  //出现错误,是不行的,缘由你们都清楚。

 


class A{};

class B : public A{};

class C : public A{};

class D : public B{};

class E : public C, public D{};

    A *pA;

    B *pB;

    E *pE = new E();

    pB = dynamic_cast<B*>(pE);

    pA = dynamic_cast<A*>(pB);  //能够

    pA = dynamic_cast<A*>(pE);  //不能够,缘由是很简单的。

    delete pE;

4.两个不相干的类之间转换。

class A {};

class B {};

    A* pa = new A;

    B* pb = dynamic_cast<B*>(pa);   // 不能够,没有相互转换的基础

可是reinterpret_cast能够转换,能够参考reinterpret_cast

 

 

const_cast:

const_cast<type_id>(expression)

这个关键字消除了几个关键字的做用const, volatile,和__unaligned的做用。const常用。MSDN有const的例子照抄过来。

class CCTest {

public:

   void setNumber( int );

   void printNumber() const;

private:

   int number;

};

void CCTest::setNumber( int num ) { number = num; }

void CCTest::printNumber() const {

   cout << "\nBefore: " << number;

   const_cast< CCTest * >( this )->number--;//这里消除了const的做用

   cout << "\nAfter: " << number;

}

int main() {

   CCTest X;

   X.setNumber( 8 );

   X.printNumber();

}

 

reinterpret_cast:

reinterpret_cast

这个关键字比较“强悍”,随意转换类型。可是转换错误,就是你的不对了。呵呵,个人原则两个字:“慎用”。

这个关键字能够在任何类型的指针之间转换。

不能够替代const_cast。

不提供安全转换。

MSDN的例子显示出来它的强悍,也显示出了他的脆弱。只要你一个不当心就会乱用。

#include <iostream>

 

// Returns a hash code based on an address

unsigned short Hash( void *p ) {

   unsigned int val = reinterpret_cast<unsigned int>( p );

   return ( unsigned short )( val ^ (val >> 16));

}

 

using namespace std;

int main() {

   int a[20];

   for ( int i = 0; i < 20; i++ )

      cout << Hash( a + i ) << endl;

}

 

 

#1 dynamic_cast的type_id为引用的状况我不许备了解,好像是微软VS2005的特性,而不是标准C++的特性。对于VS6.0我仍是感受比较欣赏的。虽然不如他的新版本那样支持更多的C++特性,可是我本身的感受是VS的产品在无限的向C#靠拢,无限的向java的易用性靠拢,这样的话C++程序在抑制到别的操做系统时就须要作很大的修改。这也是微软的霸道之处。题外话:微软的vista是一款失败的产品,在vista上微软开发了virtual PC 2007的虚拟机,可是这款产品只支持windows系统的产品安装,对于linux产品他就不支持(只能安装,不能用),由于他不支持24位真彩色。这就说明它的心态是封闭的,而封闭最终致使它的衰败。

#2 safe_cast也是微软的东西,想了解的请参考VS2005的MSDN。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/callmeback/archive/2009/04/01/4040583.aspx