********我想让一成员函数“虚拟”+“静态”,可否?********

explorer007 2002-07-15 12:59:30
RT:我想让一成员函数“虚拟”+“静态”,可否?

静态:因为该函数要作为线程函数;
虚拟:针对不同子类,需要不同的实现方法。
...全文
29 点赞 收藏 34
写回复
34 条回复
suetu 2002年07月18日
有些东西,象指针之类的,是C++不得不支持的。
有些东西,象garbage collection, 是C++内心就不想支持的
有些东西,象多继承,C++是有它自己的理由保留的(不管是不是歪理)
有些东西,象lamda closure, 是很难支持的。
但有些东西,就象这个例子中的函数类型的covariance 和contravariance, C++技术上和理论上都是可以而且应该支持的。
对成员函数,C++标准只加入了返回类型的covariance. 对参数类型的contravariance因为method overloading的原因很难支持。
但对普通函数指针,没有任何障碍。实在看不出C++为什么不支持。懒?
回复 点赞
ajoo 2002年07月18日
有些东西,象指针之类的,是C++不得不支持的。
有些东西,象garbage collection, 是C++内心就不想支持的
有些东西,象多继承,C++是有它自己的理由保留的(不管是不是歪理)
有些东西,象lamda closure, 是很难支持的。
但有些东西,就象这个例子中的函数类型的covariance 和contravariance, C++技术上和理论上都是可以而且应该支持的。
对成员函数,C++标准只加入了返回类型的covariance. 对参数类型的contravariance因为method overloading的原因很难支持。
但对普通函数指针,没有任何障碍。实在看不出C++为什么不支持。
回复 点赞
ajoo 2002年07月18日
有些东西,象指针之类的,是C++不得不支持的。
有些东西,象garbage collection, 是C++内心就不想支持的
有些东西,象多继承,C++是有它自己的理由保留的(不管是不是歪理)
有些东西,象lamda closure, 是很难支持的。
但有些东西,就象这个例子中的函数类型的covariance 和contravariance, C++技术上和理论上都是可以而且应该支持的。
对成员函数,C++标准只加入了返回类型的covariance. 对参数类型的contravariance因为method overloading的原因很难支持。
但对普通函数指针,没有任何障碍。实在看不出C++为什么不支持。
回复 点赞
wangcanhui 2002年07月18日
老兄,鱼与熊掌不可兼得!
回复 点赞
Tommy 2002年07月18日
好的。是一个好方法。我昨天也在想如何在这种情况下利用模板:)
C++的包袱太多,确实是难以十全十美的了,不像Java、C#之类那么优雅,不过还是很好的东西。
以后要多向各位高手学习学习
回复 点赞
suetu 2002年07月18日
to deal with const T*, we'd better do a C-style cast for the LPVOID.
this is a better version:

typedef unsigned int (*ThreadProc)(void*);
template<typename T>
struct ThreadHelper{
typedef unsigned int (*SafeType)(T*);
static CWinThread* beginThread(SafeType proc, T* self){
return AfxBeginThread(
reinterpret_cast<ThreadProc>(proc), (void*)self);
}
};

Then, it also works for const MonitorCore like
ThreadHelper<const MonitorCore>::beginThread(BeginThread, this);
回复 点赞
suetu 2002年07月18日
to deal with const T*, we'd better do a C-style cast for the LPVOID.
this is a better version:

typedef unsigned int (*ThreadProc)(void*);
template<typename T>
struct ThreadHelper{
typedef unsigned int (*SafeType)(T*);
static CWinThread* beginThread(SafeType proc, T* self){
return AfxBeginThread(
reinterpret_cast<ThreadProc>(proc), (void*)self);
}
};

Then, it also works for const MonitorCore like
ThreadHelper<const MonitorCore>::beginThread(BeginThread, this);
回复 点赞
ajoo 2002年07月18日
Oops! should be
typedef unsigned int (*SafeType)(T*);
typedef unsigned int (*ProcType)(void*);

Note, here, C++ shows its ugly face again. :)

According to covariance rule, SafeType should be a super type of ProcType, and a static_cast<ProcType)(proc) should compile.
But, C++ simply ignores covariance for function types.

Sigh!
回复 点赞
ajoo 2002年07月18日
sorry, it is:

template<typename T>
struct ThreadHelper{
typedef void (*SafeType)(T*);
typedef void (*ProcType)(void*);
static CWinThread* beginThread(SafeType proc, T* self){
return AfxBeginThread(
reinterpret_cast<ProcType>(proc), self);
}
};
回复 点赞
ajoo 2002年07月18日
Although it cannot be 100% type-safe when we use the void*, there's a workaround that can make it much safer:

template<typename T>
struct ThreadHelper{
typedef unsigned int (*ProcType)(T*);
typedef unsigned int (*ThreadProc)(void*);
static CWinThread* beginThread(SafeType proc, T* self){
return AfxBeginThread(
reinterpret_cast<ProcType>(proc), self);
}
};

Then, what you can do is:
ThreadHelper<MonitorCore>::beginThread(BeginThread, this);
the type-risk is carefully wrapped up. And ThreadHelper looks very type-safe.

Of course, the object's lifetime will still be a headache.
If the class supports virtual clone() method, then we may be able to clone an instance for each thread. Then no problem anymore.
回复 点赞
suetu 2002年07月17日
I am ajoo.

Tommy,
don't think there's a type-safe approach if we do need the object's state.

Your queue may work. But it's introducing too much complexity, you've got to care the thread-safety for the queue and also the lifetime of the queue.

dynamic_cast has runtime overhead. So I would not use it. It does not give us static type safety anyway.

Again, my approach only applies to the situation where you don't need to know the object state in the callback. If that's not true, then we'll have to resort to void*.

as for the lifetime, maybe ref-counting (something like IUnknown) is a solution. call AddRef before starting thread, call Release before exiting thread.
Of course, we have to do synchronization for AddRef and Release.
回复 点赞
Tommy 2002年07月17日
ajoo(聪明的一猪) :
明白你的意思了:)我也同意你的看法,我想我们的分歧在于大家对程序对上下文的要求理解不同,在不同的上下文中,就应该采用不同的处理方法才行的。我的处理方法是在我的程序中用的,目的是启动一个监控线程,程序中只有一个MonitorCore对象,是作为global对象的,所以不用担心它的生命期,也不用考虑对象的删除问题,我是在对象的析构函数中停止线程,恢复环境的。我想我还是要再看看这段程序,看能不能再改正一下,在多个对象、而且要在程序中析构时也能正常运作,现在可能可以,没考虑清楚:)我的程序是需要一些上下文的,所以只能通过LPVOID传入了,我也不想通过global变量传递,那样做的话程序以后就不好维护了。

  我还在想关于类型安全的问题,我想,能不能在基类中设置一个static的队列,每次调用BeginThread时将要处理的对象放入队列中,在BeginThread中从队列中取出。这样的话,不用通过LPVOID,也不用通过global变量,传递的对象也是类型安全的。至于在BeginThread中对象是否还存在的问题,可能要另外考虑了。既然需要这么一个上下文,程序就应该设法保证对象在那个时候还存在。

  另外,不知在我原来的做法中,对传入的LPVOID做一次dynamic_cast<MonitorCore>会不会对类型安全有帮助?我想这应该可以检测出传进来的参数是不是我想要的类型。
回复 点赞
ajoo 2002年07月17日
实践?
{
MonitorCore core;
core.Start();
}

It probably will work. But this is not guaranteed to work! It's hiding a big bug that could keep hiding for quite a long time.
回复 点赞
liuns 2002年07月17日
你可以实践一下,因为实践是检验真理的唯一标准
回复 点赞
ajoo 2002年07月17日
tommy,对复杂的需求,我真想不出有什么类型安全的机制。LPVOID的目的就是让你用传统c的弱类型方法来做。灵活,强大,但不那么安全。

你的队列的方法也许可行。但就不那么灵活了。而且,还得考虑多线程同步问题。语义上也不自然。

dynamic_cast, 我想是尽量不用。反正已经不是静态类型安全了,难道还要搭上动态的效率吗?当然,assert一下也许能好一点。

对生命期的问题,很麻烦。想来可以用一些引用计数的方法。(因为,考虑到多个线程共享同一对象的可能性),象AddRef, Release什么的。简化的IUnknown接口。但不见得非得多态。
进入线程前,AddRef, 每个线程在结束时,Release.


我的方法只适用于真正的静态函数,也就是不依赖于对象状态的。 对于更复杂的要求,就无能为力了。
回复 点赞
ajoo 2002年07月17日
tommy,对复杂的需求,我真想不出有什么类型安全的机制。LPVOID的目的就是让你用传统c的弱类型方法来做。灵活,强大,但不那么安全。

你的队列的方法也许可行。但就不那么灵活了。而且,还得考虑多线程同步问题。语义上也不自然。

dynamic_cast, 我想是尽量不用。反正已经不是静态类型安全了,难道还要搭上动态的效率吗?当然,assert一下也许能好一点。

对生命期的问题,很麻烦。想来可以用一些引用计数的方法。(因为,考虑到多个线程共享同一对象的可能性),象AddRef, Release什么的。简化的IUnknown接口。但不见得非得多态。
进入线程前,AddRef, 每个线程在结束时,Release.


我的方法只适用于真正的静态函数,也就是不依赖于对象状态的。 对于更复杂的要求,就无能为力了。
回复 点赞
ajoo 2002年07月17日
tommy,对复杂的需求,我真想不出有什么类型安全的机制。LPVOID的目的就是让你用传统c的弱类型方法来做。灵活,强大,但不那么安全。

你的队列的方法也许可行。但就不那么灵活了。而且,还得考虑多线程同步问题。语义上也不自然。

dynamic_cast, 我想是尽量不用。反正已经不是静态类型安全了,难道还要搭上动态的效率吗?当然,assert一下也许能好一点。

对生命期的问题,很麻烦。想来可以用一些引用计数的方法。(因为,考虑到多个线程共享同一对象的可能性),象AddRef, Release什么的。简化的IUnknown接口。但不见得非得多态。
进入线程前,AddRef, 每个线程在结束时,Release.


我的方法只适用于真正的静态函数,也就是不依赖于对象状态的。 对于更复杂的要求,就无能为力了。
回复 点赞
ajoo 2002年07月17日
another subtle difference, Tommy.

Your approach requires the object is still alive when the callback is called. So it'll be a headache for the programmer to determine when to delete this object.

But my approach does not require that. as long as the object is alive when getProc is called, that's enough. When the callback is called by the afxBeginThread is not a concern. You can get rid of this object after calling beginThread.

回复 点赞
zhou_hn 2002年07月16日
我觉得你采用虚拟加静态套用可以实现
回复 点赞
天外飞狐 2002年07月16日
我想你要做这件事最好在每个class中定义一个static 函数,static函数本身是一个全局函数,即使你没有使用此类,此类中的static函数在程序的任何都可以调用。所以对于static函数来说,要小心使用。
同意Tommy()
回复 点赞
发动态
发帖子
C语言
创建于2007-09-28

3.2w+

社区成员

24.0w+

社区内容

C语言相关问题讨论
社区公告
暂无公告