7.C到C++以及this指针
在C中,我们可以定义一个结构然后用相关的函数来操作这个结构,就拿C中的标准文件IO函数来说,我们可以这样写:
FILE *fp;
char ch;
fp=fopen ("C:\\a.txt","r");
ch=fgetc(fp);
fclose(fp);
这段代码读取C:\a.txt文件,并读取第一个ascii字符放到变量ch中,最后关闭文件。虽然C不支持面向对象,但是上面的文件操作却是带有面向对象观念的。这里的对象就是FILE。
这个对象由fopen创建,然后用指针fp指向它。以后每次对这个对象的操作都必须指出是针对fp的,即:函数要传递fp。C++是一样的,这个指针就是this,不过不用写出来,编译器会自动加上而已。
比如我们可以仿造上诉C中的方法来定义:
class File{
private FILE *fp;
private char fname[256],rw[5];
public File(char fname[],char rw [] ){
::strcpy(this->fname,fname);
::strcpy(this->rw,rw);
fp=::fopen (fname,rw);
}
public inline ~File(){::fclose(fp);}
public inline char getc(){return ::fgetc(fp)}
}
这样上面的C代码在C++中可以写为:
main(){
File file("C:\\a.txt","r");
char ch =file.getc();
}
显然更直截了当。为什么?因为在创建对象的时候自动调用构造函数,在调用对象的处理函数时自动传递了this指针,在file变量失效的地方自动调用了析构函数释放了对象拥有的内存和系统资源。这都是通过编译器自动插入代码实现的。通过上面的叙述,可以知道,File.getc()函数是被传入了一个指针的,这个指针指向了file变量的起始地址。
看一个例子:
在Windows编程中,如果不用MFC直接用SDK,肯定会碰到WNDCLASS结构:
typedef struct {
…
WNDPROC lpfnWndProc;
int cbClsExtra;
LPCTSTR lpszClassName;
…
} WNDCLASS;
他有一个成员:lpfnWndProc,是指向窗口过程的指针,类型是WNDPROC,定义如下:
#define CALLBACK __stdcall
typedef long LONG;
typedef LONG LRESULT;
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
注:
__stdcall表示标准调用规则,指由调用方来清理堆栈,这也保证了上一篇的变长参数传递能够实现。
还有一种是pascal调用规则,是由函数自己清理堆栈的。
如果我们定义一个类:
class Window{
protected HWND m_hwnd;
protected WNDCLASS wndclass;
public Window();
LRESULT CALLBACK WinProc (HWND, UINT, WPARAM, LPARAM);//加上static
}
定义:
Window::Window(){
wndclass. lpfnWndProc= WinProc;//这里有问题
…
m_hwnd=CreateWindow(…);….
}
是有问题的,因为WinProc已经不是WNDPROC类型的,它多了一个参数,即this。但是如果加上static,则该函数只属于类,不和对象相关,就没有this参数了,上面的赋值就合法了。但是如果用静态函数,WinProc就无法找到它要操作的对象了,这出现了一个问题:
比如要包装一个Windows窗口消息WM_PAINT:
protected virtual bool OnPaint();
在WinProc中:
switch(){
…
case WM_PAINT:this->OnPaint();//这句就无法调用了,因为没有this
…
}
我觉得可以在Windows类里面放上一个static的映射表:把hwnd和其对应的Window对象this指针关联起来,然后在WinProc中根据hwnd查找映射表得到this指针。每次创建一个以Window为基类的对象就添加进去一对值:(hwnd,this)。当然这是一种很简单的做法。
MFC的消息传递没用虚函数,蛮复杂,它把所有的WNDCLASS的WNDPROC都赋值为同一个全局的winproc然后通过查找MASSAGE_MAP表来查找相应的处理函数,这样每次建立一个类就要加一张表然后还要添上处理函数的指针,也很麻烦,好在VC能自动完成这些工作。其中,在AfxWndProc中是使用CWnd::FromHandlePermanent(hWnd)得到pWnd也就是this指针的。
在C中,我们可以定义一个结构然后用相关的函数来操作这个结构,就拿C中的标准文件IO函数来说,我们可以这样写:
FILE *fp;
char ch;
fp=fopen ("C:\\a.txt","r");
ch=fgetc(fp);
fclose(fp);
这段代码读取C:\a.txt文件,并读取第一个ascii字符放到变量ch中,最后关闭文件。虽然C不支持面向对象,但是上面的文件操作却是带有面向对象观念的。这里的对象就是FILE。
这个对象由fopen创建,然后用指针fp指向它。以后每次对这个对象的操作都必须指出是针对fp的,即:函数要传递fp。C++是一样的,这个指针就是this,不过不用写出来,编译器会自动加上而已。
比如我们可以仿造上诉C中的方法来定义:
class File{
private FILE *fp;
private char fname[256],rw[5];
public File(char fname[],char rw [] ){
::strcpy(this->fname,fname);
::strcpy(this->rw,rw);
fp=::fopen (fname,rw);
}
public inline ~File(){::fclose(fp);}
public inline char getc(){return ::fgetc(fp)}
}
这样上面的C代码在C++中可以写为:
main(){
File file("C:\\a.txt","r");
char ch =file.getc();
}
显然更直截了当。为什么?因为在创建对象的时候自动调用构造函数,在调用对象的处理函数时自动传递了this指针,在file变量失效的地方自动调用了析构函数释放了对象拥有的内存和系统资源。这都是通过编译器自动插入代码实现的。通过上面的叙述,可以知道,File.getc()函数是被传入了一个指针的,这个指针指向了file变量的起始地址。
看一个例子:
在Windows编程中,如果不用MFC直接用SDK,肯定会碰到WNDCLASS结构:
typedef struct {
…
WNDPROC lpfnWndProc;
int cbClsExtra;
LPCTSTR lpszClassName;
…
} WNDCLASS;
他有一个成员:lpfnWndProc,是指向窗口过程的指针,类型是WNDPROC,定义如下:
#define CALLBACK __stdcall
typedef long LONG;
typedef LONG LRESULT;
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
注:
__stdcall表示标准调用规则,指由调用方来清理堆栈,这也保证了上一篇的变长参数传递能够实现。
还有一种是pascal调用规则,是由函数自己清理堆栈的。
如果我们定义一个类:
class Window{
protected HWND m_hwnd;
protected WNDCLASS wndclass;
public Window();
LRESULT CALLBACK WinProc (HWND, UINT, WPARAM, LPARAM);//加上static
}
定义:
Window::Window(){
wndclass. lpfnWndProc= WinProc;//这里有问题
…
m_hwnd=CreateWindow(…);….
}
是有问题的,因为WinProc已经不是WNDPROC类型的,它多了一个参数,即this。但是如果加上static,则该函数只属于类,不和对象相关,就没有this参数了,上面的赋值就合法了。但是如果用静态函数,WinProc就无法找到它要操作的对象了,这出现了一个问题:
比如要包装一个Windows窗口消息WM_PAINT:
protected virtual bool OnPaint();
在WinProc中:
switch(){
…
case WM_PAINT:this->OnPaint();//这句就无法调用了,因为没有this
…
}
我觉得可以在Windows类里面放上一个static的映射表:把hwnd和其对应的Window对象this指针关联起来,然后在WinProc中根据hwnd查找映射表得到this指针。每次创建一个以Window为基类的对象就添加进去一对值:(hwnd,this)。当然这是一种很简单的做法。
MFC的消息传递没用虚函数,蛮复杂,它把所有的WNDCLASS的WNDPROC都赋值为同一个全局的winproc然后通过查找MASSAGE_MAP表来查找相应的处理函数,这样每次建立一个类就要加一张表然后还要添上处理函数的指针,也很麻烦,好在VC能自动完成这些工作。其中,在AfxWndProc中是使用CWnd::FromHandlePermanent(hWnd)得到pWnd也就是this指针的。