老钟古

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

http://blog.csdn.net/callmeback/archive/2009/03/31/4039304.aspx

 


说实话,从来没有感觉到这个关键字有用,直到今天。

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

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

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

class gxgExplicit  //没有关键字explicit的类

{

public:

   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

 

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

 

posted on 2010-09-19 22:13  老钟古  阅读(639)  评论(0编辑  收藏  举报