类成员函数作为线程函数使用
C++类成员函数使用时,都会隐式传递一个this指针给该函数,this指针指向该类的对象。函数体可以通过显示调用该指针或直接访问类内成员。
回调函数是用指针来调用的函数,最常使用的回调函数就是在创建线程时,以一个函数指针以及传递给这个函数多个参数来调用线程函数来创建线程。那么一般的类成员函数是不能用作回调函数的,因为库函数在使用回调函数时,都会传递指定的符合回调函数声明的的参数给回调函数,而类成员函数隐式包含一个this指针参数,所以把类成员函数当作回调函数编译时因为参数不匹配会出错。
如果类内成员函数调用回调函数怎么实现呢?一般有两种方法:
1.回调函数声明在类外,全局的传类对象指针给回调函数即可使用类内成员,因为回调函数是全局的所以影响了类的封装性
2.类内成员函数作为回调函数
利用关键名static声明类内成员函数,因为类静态成员函数只能操作类静态成员,所以函数若想访问类成员,只能通过调用函数传递类对象指针给回调函数,或者在类内声明一个静态指针,该指针指向类,然后在函数中调用该指针访问类成员。
但是上面两种方法的使用还是线程并不属于对象,还是属于类。
我这里要说的是将一个类的成员函数作为线程函数使用,并不是以上面两种。
因为一般都是用静态函数作为线程的回调函数实现,但是有时候总是感觉不是很顺畅,更改,就好像破坏了类的封装性,不改,访问实在是麻烦。所以,今天要做的就是让类的成员函数作为线程的回调函数存在,其中使用的一个比较特殊的结构就是
union
{
void (*ThreadProc)(LPVOID pvParam);
void (student::*MemberProc)(LPVOID pvParam);
} Proc;
联合类,用于转换类成员方法指针到普通函数指针。
方法1、下面看实例:下面例子中用printInfo作为线程回调函数
//ThreadMember.cpp : 定义控制台应用程序的入口点。
#include“stdafx.h”
#include<string>
#include<Windows.h>
#include<iostream>
using namespacestd;
class student
{
public:
student()
{
m_handle = NULL;
name = “Member fun is ThreadFun.”;
age = 13;
}
void printInfo(LPVOID pvParam); //作为线程回调函数
void startUp();
private:
HANDLE m_handle;
int age;
string name;
};
union //用于转换类成员方法指针到普通函数指针(地址相同)
{
void ( *ThreadProc)(LPVOID pvParam);
void (student::*MemberProc)(LPVOID pvParam);
} Proc;
void student::printInfo(LPVOID pvParam)
{
student * pS = (student * )pvParam;
while( WaitForSingleObject(pS->m_handle,30000) != WAIT_OBJECT_0 ) //线程每隔一时间执行,如果要唤醒就加一个事件,改变信号状态
{
cout<< “age” <<“” <<pS->age<< endl;
cout<< “name” <<” ” <<pS->name<< endl;
Sleep(2000);
}
}
voidstudent::startUp()
{
Proc.MemberProc =&student::printInfo;//指向成员函数
m_handle = CreateThread(NULL,0,LPTHREAD_START_ROUTINE(Proc.ThreadProc),this,0,0);
// ThreadProc和MemberProc在共联体类中,所以也间接指向成员函数
}
int _tmain(intargc, _TCHAR* argv[])
{
student s1;
s1.startUp();
system(“pause”);
_CrtDumpMemoryLeaks();
return 0;
}
方法2、通过友元函数实现。
//ThreadMember.cpp : 定义控制台应用程序的入口点。
#include“stdafx.h”
#include<string>
#include<Windows.h>
#include<iostream>
using namespace std;
class student
{
public:
student()
{
m_handle = NULL;
name = “Member fun is ThreadFun.”;
age = 13;
}
friend UINT WINAPIprintInfo(LPVOID pvParam);
void startUp();
private:
HANDLE m_handle;
int age;
string name;
};
UINT WINAPI printInfo (LPVOID pvParam)
{
student * pS = (student * ) pvParam;
while(true ){
cout <<“age”<<pS-> age<<endl ;
cout <<“name”<<pS->name <<endl;
Sleep (2000);
}
return 0 ;
}
void student::startUp()
{
m_handle =CreateThread(NULL,0,LPTHREAD_START_ROUTINE(printInfo),this,0,0);
}
int _tmain(int argc, _TCHAR*argv[])
{
student s1;
s1.startUp();
system(“pause”);
_CrtDumpMemoryLeaks();
return 0;
}
以上两种方法可以讲一个类的成员函数作为线程函数去执行, 从而让对象维护线程。
且要做到线程同步,要创建一个自动重置类型的时间对象。如果是人工重置事件对象,当一个线程等待到一个人工重置的事件对象后这个事件对象任然处于有信号状态,所以其他线程也可以得到该事件对象。