c++中,类成员函数作为线程函数 。网上的一段代码研究
这段代码可以运行。使用非静态方法后,传入的this指针没有使用,非传入的this对象调用自己的成员函数。!!!!
#include "stdafx.h"
#include <stdio.h>
#include <process.h>
class TMyClass {
int m_nCount;
int m_nId;
public:
TMyClass(int nId, int nCount)
:m_nId(nId), m_nCount(nCount)
{
m_nId = nId;
m_nCount = nCount;
}
//感觉有问题,传入的this指针没有使用。这个从表面来看用了非静态方法,其实意义不大。
void ThreadProc() // 类成员方法
{
for (int i = 0; i < m_nCount; i++) // 根据m_nCount成员打印一排数字
{
printf("Class%d : %d\n", m_nId, i);
}
}
};
int main(int argc, char* argv[])
{
union { // 联合类,用于转换类成员方法指针到普通函数指针(试过编译器不允许在这两种函数之间强制转换),不知道有没有更好的方法。
void ( *ThreadProc)(void *);
void ( TMyClass::*MemberProc)();
} Proc; // 尽管联合里的两种函数类型现在看起来有很大不同,但它们的最终形式是相同的。
TMyClass MyClass1(1, 10), MyClass2(2, 5); // 产生两个TMyClass对象
Proc.MemberProc = &TMyClass::ThreadProc; // 转换,Proc.ThreadProc就是对应的普通函数指针了
_beginthread(Proc.ThreadProc, 4096, &MyClass1); // 开始线程,这里的Proc.ThreadProc实际上是TMyClass::ThreadProc, 它要的this指针是我们给的&MyClass1。
//_beginthread(Proc.ThreadProc, 4096, &MyClass2);
system("pause");
return 0;
}
#include "stdafx.h"#include <stdio.h>#include <process.h>
class TMyClass {int m_nCount;int m_nId;public:TMyClass(int nId, int nCount):m_nId(nId), m_nCount(nCount){m_nId = nId;m_nCount = nCount;
}//感觉有问题,传入的this指针没有使用。这个从表面来看用了非静态方法,其实意义不大。void ThreadProc() // 类成员方法{for (int i = 0; i < m_nCount; i++) // 根据m_nCount成员打印一排数字{printf("Class%d : %d\n", m_nId, i);}}};
int main(int argc, char* argv[]){union { // 联合类,用于转换类成员方法指针到普通函数指针(试过编译器不允许在这两种函数之间强制转换),不知道有没有更好的方法。void ( *ThreadProc)(void *);void ( TMyClass::*MemberProc)();} Proc; // 尽管联合里的两种函数类型现在看起来有很大不同,但它们的最终形式是相同的。
TMyClass MyClass1(1, 10), MyClass2(2, 5); // 产生两个TMyClass对象
Proc.MemberProc = &TMyClass::ThreadProc; // 转换,Proc.ThreadProc就是对应的普通函数指针了
_beginthread(Proc.ThreadProc, 4096, &MyClass1); // 开始线程,这里的Proc.ThreadProc实际上是TMyClass::ThreadProc, 它要的this指针是我们给的&MyClass1。//_beginthread(Proc.ThreadProc, 4096, &MyClass2);system("pause");return 0;}
====================================================================================================================================================================================================================================================================================================================================================================
类成员函数作为pthread_create函数参数
http://blog.csdn.net/luo6620378xu/article/details/8521940
近日需要将线程池封装成C++类,类名为Threadpool。在类的成员函数exec_task中调用pthread_create去启动线程执行例程thread_rounter。编译之后报错如下:
spfs_threadpool.cpp: In member function ‘int Threadpool::exec_task(task*)’:
spfs_threadpool.cpp:174: error: argument of type ‘void* (Threadpool::)(void*)’ does not match ‘void* (*)(void*)’
出现类型不匹配的问题。因为pthread_create需要的参数类型为void* (*)(void*),而thread_rounter作为类的成员函数时其类型是void* (Threadpool::)(void*)的成员函数指针。我们知道类的成员函数在经过编译器处理之后,会变成带有this指针参数的全局函数,所以类型注定是不会匹配的。但是如果将thread_rounter声明为static类型,那么编译器会将static形式的函数,转换成不带this指针的全局函数,所以其类型可以与pthread_create需要的参数类型相匹配。但是类的静态成员函数无法访问类的非静态成员,不过这可以通过传递this指针解决这个问题。
综上,我的这个问题可以这个样子解决。
出问题之前的代码:
void *thread_rounter(void *)//线程执行函数
{
//直接访问类的成员
}
exec_task函数中调用:
pthread_create(&tid,NULL,thread_rounter,NULL);//启动线程执行例程
修复这个问题的代码:
static void *thread_rounter(void *tmp)/线程执行函数
{
Threadpool *p=(Threadpool *)tmp;
//通过p指针间接访问类的非静态成员
}
exec_task函数中调用:
pthread_create(&tid,NULL,thread_rounter,(void *)this);//启动线程执行例程
----------------------------------------------------------------------------------------------------------------------
在网上搜索一下还有其他的解决方案,摘录如下,为了以示尊重标明文章来源,感谢原文作者。
方案二:
将线程启动函数声明为模板函数。
摘录自:http://hi.baidu.com/angelevil2006/item/e1806ec30574ff11515058d1
[cpp] view plain copy
- template <typename TYPE, void (TYPE::*_RunThread)() >
- void* _thread_t(void* param)//线程启动函数,声明为模板函数
- {
- TYPE* This = (TYPE*)param;
- This->_RunThread();
- return NULL;
- }
- class MyClass
- {
- public:
- MyClass();
- void _RunThread();
- private:
- pthread_t tid;
- };
- void MyClass::_RunThread()
- {
- this->DoSomeThing();
- }
- MyClass::MyClass()
- {
- pthread_create(&tid, NULL, _thread_t<MyClass, &MyClass::_RunThread>, this);
- }
- //函数模版不单可以替换类型本身,还能替换类型的成员函数。
- //注意: 1、名称只能是_RunThread,不能在指定模版参数的时候修改;
- // 2、_RunThread只能是public的,除非把_thread_t定义到MyClass的内部。
采取这个方案放入我的项目中:
出问题之前的代码:
void *thread_rounter(void *)//线程执行函数
{
//直接访问类的成员
}
exec_task函数中调用:
pthread_create(&tid,NULL,thread_rounter,NULL);//启动线程执行例程
修复这个问题的代码:
添加public成员函数:
void Run()
{
//该函数替换上述thread_rounter的执行代码
}
thread_rounter修改为全局模板函数:
template <typename TYPE, void (TYPE::*Run)() >
void * thread_rounter(void * param)
{
TYPE *p=(TYPE*)param;
p->Run();
return NULL;
}
exec_task函数中调用:
pthread_create(&tid,NULL,thread_rounter<Threadpool,&Threadpool::Run>,(void *)this);
总结:
解决这个问题的关键在于想方设法使启动函数指针满足void*(*)(void *)类型。
将启动函数改写成static成员函数适用于可以修改类的源代码的情况。
而将启动函数写成模板函数还可以适用于没有类的源代码的情况,自己写一个类,公共继承自原类,添加启动函数为模板函数即可。
=============================================================================================================================================================================================================================================================================================================================================================================================================================================================
C++中 线程函数为静态函数 及 类成员函数作为回调函数
线程函数为静态函数:
线程控制函数和是不是静态函数没关系,静态函数是在构造中分配的地址空间,只有在析构时才释放也就是全局的东西,不管线程是否运行,静态函数的地址是不变的,并不在线程堆栈中static只是起了一个装饰的作用,所以二者并没有必然的关系
线程也是一种资源,是受系统调度的。因此,你必须要让系统知道你的线程的起始位置,即线程函数的指针。Window系统的接口(API)是C标准的,系统在启动线程时,需要直接得到线程的起始位置,因此你也必须向系统直接传递这样一个直接的函数指针,这与C++其它的特性是无关的。
因为对于非静态成员函数而言,默认情况下,参数列表中都会有一个this指针,例如fun(自定义参数),实际上编译后就变成这样类型:fun(自定义参数,某个类 * this)。这样编译就会出错,多了一个参数,所以这个函数就不能作为线程函数了。加上static修饰之后,类的成员函数就不会加上默认this了,所以符合调用规定。
在C++的类中,普通成员函数不能作为pthread_create的线程函数,如果要作为pthread_create中的线程函数,必须是static !在C语言中,我们使用pthread_create创建线程,线程函数是一个全局函数,所以在C++中,创建线程时,也应该使用一个全局函数。static定义的类的成员函数就是一个全局函数。
类成员函数作为回调函数
回调函数是基于C编程的Windows SDK的技术,不是针对C++的,程序员可以将一个C函数直接作为回调函数,但是如果试图直接使用C++的成员函数作为回调函数将发生错误,甚至编译就不能通过。
普通的C++成员函数都隐含了一个传递函数作为参数,亦即“this”指针,C++通过传递一个指向自身的指针给其成员函数从而实现程序函数可以访问C++的数据成员。这也可以理解为什么C++类的多个实例可以共享成员函数但是确有不同的数据成员。由于this指针的作用,使得将一个CALLBACK型的成员函数作为回调函数安装时就会因为隐含的this指针使得函数参数个数不匹配,从而导致回调函数安装失败。
这样从理论上讲,C++类的成员函数是不能当作回调函数的。但我们在用C++编程时总希望在类内实现其功能,即要保持封装性,如果把回调函数写作普通函数有诸多不便。经过网上搜索和自己研究,发现了几种巧妙的方法,可以使得类成员函数当作回调函数使用。
这里采用Linux C++中线程创建函数pthread_create举例,其原型如下:
int pthread_create( pthread_t *restrict tidp , const pthread_attr_t *restrict attr , void* (*start_rtn)(void*) , void *restrict arg );
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址,即回调函数。
最后一个参数是运行函数的参数。
这里我们只关注第三个参数start_run,它是一个函数指针,指向一个以void*为参数,返回值为void*的函数,这个函数被当作线程的回调函数使用,线程启动后便会执行该函数的代码。
方法一:回调函数为普通函数,但在函数体内执行成员函数
class MyClass { pthread_t TID; public: void func() { //子线程执行代码 } bool startThread() {//启动子线程 int ret = pthread_create( &TID , NULL , callback , this ); if( ret != 0 ) return false; else return true; } }; static void* callback( void* arg ) {//回调函数 ((MyClass*)arg)->func();调用成员函数 return NULL; } int main() { MyClass a; a.startThread(); }
类MyClass需要在自己内部开辟一个子线程来执行成员函数func()中的代码,子线程通过调用startThread()成员函数来启动。这里将回调函数callback写在了类外面,传递的参数是一个指向MyClass对象的指针(在pthrad_create()中由第4个参数this指定),回调函数经过强制转换把void*变为MyClass*,然后再调用arg->func()执行子线程的代码。
这样做的原理是把当前对象的指针当作参数先交给一个外部函数,再由外部函数调用类成员函数,以外部函数作为回调函数,但执行的是成员函数的功能,这样相当于在中间作了一层转换。缺点是回调函数在类外,影响了封装性,这里把callback()限定为static,防止在其它文件中调用此函数。
方法二:回调函数为类内静态成员函数,在其内部调用成员函数
在方法一上稍作更改,把回调函数搬到类MyClass里,这样就保持了封装性。代码如下:
class MyClass { static MyClass* CurMy;//存储回调函数调用的对象 static void* callback(void*);//回调函数 pthread_t TID; void func() { //子线程执行代码 } void setCurMy() {//设置当前对象为回调函数调用的对象 CurMy = this; } public: bool startThread() {//启动子线程 setCurMy(); int ret = pthread_create( &TID , NULL , MyClass::callback , NULL ); if( ret != 0 ) return false; else return true; } }; MyClass* MyClass::CurMy = NULL; void* MyClass::callback(void*) { CurMy->func(); return NULL; } int main() { MyClass a; a.startThread(); }
类MyClass有了1个静态数据成员CurMy和1个静态成员函数callback。CurMy用来存储一个对象的指针,充当方法一中回调函数的参数arg。callback当作回调函数,执行CurMy->func()的代码。每次建立线程前先要调用setCurMy()来让CurMy指向当前自己。
这个方法的好处时封装性得到了很好的保护,MyClass对外只公开一个接口startThread(),子线程代码和回调函数都被设为私有,外界不可见。另外没有占用callback的参数,可以从外界传递参数进来。但每个对象启动子线程前一定要注意先调用setCurMy()让CurMy正确的指向自身,否则将为其它对象开启线程,这样很引发很严重的后果。
方法三:对成员函数进行强制转换,当作回调函数
class MyClass { pthread_t TID; void func() { //子线程执行代码 } public: bool startThread() {//启动子线程 typedef void* (*FUNC)(void*);//定义FUNC类型是一个指向函数的指针,该函数参数为void*,返回值为void* FUNC callback = (FUNC)&MyClass::func;//强制转换func()的类型 int ret = pthread_create( &TID , NULL , callback , this ); if( ret != 0 ) return false; else return true; } }; int main() { MyClass a; a.startThread(); }
这个方法是原理是,MyClass::func最终会转化成 void func(MyClass *this); 也就是说在原第一个参数前插入指向对象本身的this指针。可以利用这个特性写一个非静态类成员方法来直接作为线程回调函数。对编译器而言,void (MyClass::*FUNC1)()和void* (*FUNC)(void*)这两种函数指针虽然看上去很不一样,但他们的最终形式是相同的,因此就可以把成员函数指针强制转换成普通函数的指针来当作回调函数。在建立线程时要把当前对象的指针this当作参数传给回调函数(成员函数func),这样才能知道线程是针对哪个对象建立的。
方法三的封装性比方法二更好,因为不涉及多个对象共用一个静态成员的问题,每个对象可以独立地启动自己的线程而不影响其它对象。
====================================================================================================================================================================================================================================================================================================================================================================ttemplate <class T>//结构体模板
struct node
{
T data;
struct node<T> *lchild;
struct node<T> *rchild;
};
template <class T>//类内类模板
class bin
{
private:
node <T> h;
public:
void pri();
};
template <class T>//类外成员函数实现模板
void bin<T>::pri()
{
node<T> *p = new node;
cout<<"qunimade"<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
bin<int> abc;
}
===================================================================================================================================================================================================================================================================================================================================================================
C++静态成员函数访问非静态成员的几种方法
大家都知道C++中类的成员函数默认都提供了this指针,在非静态成员函数中当你调用函数的时候,编译器都会“自动”帮你把这个this指针加到函数形参里去。当然在C++灵活性下面,类还具备了静态成员和静态函数,即
class A { public: static void test() { m_staticA += 1; } private: static int m_staticA; int m_a };
此时你的test函数只能去访问m_staticA成员,而不能去访问m_a。同学可能会问,这算什么问题?问题都是在应用场景中才能体现的,我一开始也是不以为意,直到我遇到了回调函数这个烦人的问题我才真正静下心来去考虑这个知识点。
先简单说下回调,在座的应该都知道回调的含义,在C中回调主要体现就是回调函数,当然C++中也有仿函数等其他用法,抛开这些,单纯在回调函数这个点上我们进行如下讨论。
由于C++类的成员函数都隐含了this指针,如果我直接注册,比如
typedef void (A::*FunPtr)(); FunPtr p = A::hello; p();
此时程序会报错,提示信息是你缺少一个this指针,意味着你要真的想使用p,你必须有一个分配好空间的实例才能来调用
typedef void (A::*FunPtr)(); FunPtr p = A::hello; A a; A *pA = new A(); (a.*p)(); (pA->*p)();
当然,如果仅仅是对C++的类静态函数进行回调函数注册,你是不需要考虑this指针的
typedef void (A::*FunPtr)(); FunPtr p = A::test; p();
但问题就是,你此时的静态函数是不能拥有你的成员变量的,看到了吧,问题来了。面对这种需求,我们就真正应该静下心来好好想想,究竟如何才能让静态函数去访问非静态成员变量这个问题了。
方法一:
有一个很取巧的办法,就是在静态函数的形参表里加上实例的地址,也就是
class A { public: static void test(A *a) { a->m_a += 1; } void hello() { } private: static int m_staticA; int m_a };
这样在你回调函数的时候,你可以通过这个来让本身不能访问成员非静态变量的静态函数(太拗口)来访问非静态成员变量。
方法二:
其实这个方法在GLIB中用的很多,就是放上全局变量地址即
A g_a; class A { public: static void test() { g_a.m_a += 1; } void hello() { } private: static int m_staticA; int m_a };
这种方法我们了解就好,全局变量我们并不推荐。
方法三:
大家都知道静态成员函数不能访问非静态成员,但别忘了,他们可以访问静态成员,也就是说,如果我们的这个类是个单例,我们完全可以在创建的时候把this指针赋值给那个静态成员,然后在静态成员函数内部就可以放心大胆的使用了。
class A { public: A() { m_gA = this; } static void test() { m_gA.m_a += 1; } void hello() { } private: static int m_staticA; static A *m_gA; int m_a };
方法四:
和方法一比较像,但他的方向思想更多的是针对内存块这个概念,意思就是在静态函数的形参比加上一个void *的内存首地址,然后在内部做转换
class A { public: static void test(void *pData) { A *a = (A *)pData; a->m_a += 1; } void hello() { } private: static int m_staticA; int m_a }; A a; test(&a);
如上,我整理了4种方法,当然方法还有很多,其实绕了这么大远路,我们的希望就是不破坏回调函数整洁的函数接口(加上自己的实例指针)而做的妥协,如果你更喜欢通过改变接口或者通过用Java类似的interface方式来实现,那也没有问题,这里主要就是提供给大家一个思路,C++确实很灵活,我们要用好这把双刃剑 : )
=====================================================================================================================================================================================================================================================================================================================================================================
C++静态函数中调用非静态成员函数或变量
2016年10月21日 16:29:21 xx780452700 阅读数:8758
用static声明的函数是静态函数,在调用该函数时,不会将对象的引用传递给它,所以在static函数中不可访问非static的成员
但实际情况中,我们经常会需要在静态函数中使用非静态函数或变量。
如下代码中ThreadProc是静态函数,线程启动时会自动调用
在此函数中需使用非静态成员变量m_nMark ,此时不可以在ThreadProc中直接调用。
解决方法:先声明一个自己类型的静态变量m_staticSelf,然后对其进行this赋值,最后在静态函数中调用对应m_staticSefl中的函数或变量即可,其实就是间接的调用自己的函数或变量。
.h
class RecorderNative
{
private:
static RecorderNative* m_staticSelf;
int m_nMark;
public:
RecorderNative();
~RecorderNative();
private:
static DWORD WINAPI ThreadProc(LPVOID lpParam);
void Process( );
};
.cpp
RecorderNative* RecorderNative::m_staticSelf;
RecorderNative::RecorderNative()
{
m_staticSelf = this;
}
RecorderNative::~RecorderNative()
{
}
DWORD WINAPI RecorderNative::ThreadProc(LPVOID lpParam)
{
m_staticSelf->Process();
return 0;
}
void RecorderNative::Process( )
{
m_nMark = 1;
}