Chinaunix首页 | 论坛 | 博客
  • 博客访问: 572968
  • 博文数量: 155
  • 博客积分: 4015
  • 博客等级: 上校
  • 技术积分: 1625
  • 用 户 组: 普通用户
  • 注册时间: 2005-11-18 16:55
文章分类

全部博文(155)

文章存档

2009年(20)

2008年(39)

2007年(66)

2006年(29)

2005年(1)

我的朋友

分类: C/C++

2007-09-28 11:46:12

   转型(Cast)可能破坏C++面向对象最根本的特征之一----封装(Encyption).特别是当我们使用了一个“蛮力”的转型时更是如此。下面我们通过一小段代码看看转型对封装的破坏。考虑以下面的这个类:
 

class hide
{
public:
    hide(int _n, string _msg):errNo(_n),msg(_msg)
    {}
private:
    int errNo;
    string msg;
};

类作者本意是想封装errNo和msg,这符合面向对象的思想,是个较好的主意。但若是通过一些"蛮力"的转型仍可以将errNo,msg水落石出。
  为了让errNO,msg显形,我们定义以下类:
 

class visual
{
public:
    visual(int _n, string _msg):errNo(_n),msg(_msg)
    {}
public: //Only change access level

    int errNo;
    string msg;
};

 
在这个类中我们只做了小小的变动将errNo及msg的访问权限更改为public.破坏工具制做完成了,下面开始我们的破坏之旅,看以下代码:
 
 

//Platform: WinXP + VC6.0
#include <iostream>
#include <string>
using namespace std;

int main()
{
    hide h(100,string("hello Jerry"));

    visual *v1 = (visual*)&h;                    //C-style,蛮力转型
    visual *v2 = reinterpret_cast<visual*>(&h); //C++-style,蛮力转型
    //visual *v3 = static_cast(&h); //C++-style,理性转型

    cout<<v1->errNo<<endl;
    cout<<v1->msg<<endl;
    v1->errNo = 222;
    cout<<v2->errNo<<endl;
    cout<<v2->msg<<endl;

    return 0;
}

 
运行结果让我们大吃一惊,精心封装的errNo及msg就这样被显示于众了。hide这个类名不符实呀!
 
   上面的代码采用了三种转型(Cast),分别是:C风格的转型,C++风格的reinterpret_cast和static_cast。前两种转型即是我所说的“蛮力”转型。他们可以成功将h对象所封装的部分显示于众。static_cast转型是一类较为理性的C++风格的转型,它拒绝没有继承关系的类之间的"蛮力"转型,如果我们将第三种转型的注释去掉,Compiling时将会出现以下错误信息:
error C2440: 'static_cast' : cannot convert from 'class hide *' to 'class visual *'
   在这里我也再次推荐static_cast这种C++-style的转型,这种转型可以给你减少你在无意间使用了“蛮力”转型而带来的问题。
   有没有方法可以防止这类转型呢?之所以"蛮力"转型会成功,主要是因为在C++中访问等级(Access Level)并不影响类对象在内存中的布局。我们可以采取一些小手来改变hide在内存上的布局来防止这种“蛮力”转型,例如我们可以在hide中加入一个无意义的virtual函数,这样使hide对象的内存布局和visual类对象的内存布局不一致,这样在做转型时就会出现系统错误。
   不过上面的只是理论而已并不实用,最简单的方式还是程序员在写C++程序时尽量减少转型动作,在必须做转型时优先考虑那个"理性"的转型,而避免"蛮力"的转型。
 
 
 
 
 
 
 
 
Jerry.Chow
      9/28'07
  
阅读(900) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~