野男人

不想做CEO的程序员不是一个好的产品经理

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

类的一个特征就是数据与方法封装在一起,在编译器越来越智能的今天,大多数程序员已经习惯了数据与方法“本来”就是一个“整体”的概念。事实是不是这样呢?在面向对象语言出现之前的程序世界又是什么样的呢?让我们先回到C的世界中,看看那里的数据与方法(C中称为函数)是什么样子的。

    C中通常定义一个结构体用来把一些数据组合在一块儿,然后定义一个函数处理这个结构体。例如:

 

/* 版本1*/

 

typedef struct _SampleData
{
    
int data1;

    
char data2[10];

}SampleData;

 

 

/*函数定义,打印SampleData结构的数据*/

void ProcessData()
{

    SampleData data;

    data.data1 
= 12;

    strcpy(data.data2,
"123456789");

    printf(
"data1=%d, data2=%s ", data.data1, data.data2);

 }

 

 

 

可以看到数据与对数据的操作是分离的,只要把数据所在的文件包含进来,就可以任意定义一个函数来进行操作。

 

接着再看看用C++ 把数据与方法封装在一起的代码形式。

// C++ 版本

class SampleData

{

public:

    SampleData(){};

    
~SampleData(){};

    
void ProcessData()

    { 

        data1 
= 12

        strcpy(data2,
"123456789");

        cout
<<"data1="<<data1<<endl;

        cout
<<"data2="<<data2<<endl;

    }

private:

    
int data1;

    
char data2[10];

};

 

C++ 可以把数据和对数据的操作整合到一个class 中。能否在C struct 中做到跟C++ class 一样,把ProcessData() 与 数据变量整合在一起呢?模仿上面的类定义,我们可能会尝试定义一个这样的数据结构:

 

/* 版本2*/

 

typedef struct _SampleData
{
    
int data1;
    
char data2[10];
    
void ProcessData();   //声明操作
}
SampleData;

 

ProcessData() 函数的实现依然跟【版本1】一样。编译,链接。真好,没有出现任何的错误。是否这样就可以了呢?噢,忘记了一个重要的步骤,还没有在任何地方使用过这个SampleData结构体的ProcessData 操作呢。OK, 没有关系,在主函数声明调用一下吧。大概像下面这样:

void  main()
{
    SampleData data;
    data.ProcessData();
}

 

再编译,通过。链接,出现错误:unresolved external symbol "public: void __thiscall _SampleData::ProcessData(void)" (?ProcessData@_SampleData@@QAEXXZ)。编译器找不到结构体中声明的ProcessData 函数的具体实现,看来现在这样调用data.ProcessData() 是不行的。编译器并没有我们想象的那样自动地把 ProcessData 的声明与它的实现对应起来。那么怎样做才能达到预先的目的呢?

    如果能把ProcessData()函数的地址赋给 SampleData 结构里的操作声明,那么一切问题都解决了。看来【版本2】也需要改进一下。

 

/* 版本3*/

 

typedef struct _SampleData

{

    
int data1;

    
char data2[10];

    
void (*ProcessData)();   //注意,已经改为ProcessData的函数指针

}SampleData;

 

主函数也得改一下,在使用data.ProcessData() 之前,把函数ProcessData的地址赋给结构体里的函数指针变量。结果如下:

void  main()

{

SampleData data;

    data.ProcessData 
= ProcessData;   //注意,初始化函数指针变量

    data.ProcessData();

}

 

再编译、链接,终于通过。

到这里你已经认为这样成功的实现了上面定义的SampleDataC++ 版本。不过不要高兴的太早,做个测试看看。

void  main()
{

    SampleData data;

    data.data1 
= 22;   // step 1

    strcpy(data.data2 , 
"987654321");   // step 2

    data.ProcessData 
= ProcessData;   //step 3

    data.ProcessData();   
//step 4 

}

 

按预先的设想,执行完step 4 后应该打印出这样的结果:data1=22, data2=987654321

而实际上打印出的是:data1=12, data2=123456789 。原因很明显,ProcessData 函数里面有个 SampleData的局部变量。也就是说,结构体里的方法data.ProcessData() 打印出的内容并不是结构体本身的变量,而是这个局部变量的内容。

    那怎样才能打印出结构体内变量的内容呢?C++里编译器为类的方法加上了 this 指针,所以我们不用去担心这个问题。而在C的世界里,编译器并没有这样做。那么就需要显示的增加一个结构指针,代替C++中类似this指针的功能。接下来再改变一下SampleData结构体的定义:

 

/* 版本4*/

 

typedef struct _SampleData
{
    
int data1;

    
char data2[10];

    
void (*ProcessData)(struct _SampleData * thisSample);//增加了一个本身的指针参数

}SampleData;

 

函数的实现也需要修改,它看起来像下面这个样子:

void ProcessData(struct _SampleData * thisSample)
{

    printf(
"data1=%d, data2=%s ", thisSample->data1, thisSample->data2);

}

 

 

接着在主函数的调用方式也要修改:

 

   data.ProcessData(&data);   //传入data变量的地址。

 

看看运行结果,OK,这才是我们所想要的功能。而 thisSample这个指向自己的指针就是C++this指针的实现形式,只不过在C里要自己实现,而在C++是编译器自动为我们加上了,看到这里你也应该已经理解C++this指针的概念了吧。

看来在机器的世界里数据与对数据的操作是分开的,只是由于在编译器的支持下,才给程序员展现出了一个完美的对象。

posted on 2008-08-22 15:55  野男人  阅读(10950)  评论(4编辑  收藏  举报