模板成员函数 和 虚函数 不能共存

aj3423 2010-08-09 08:26:47
东西快写完才发觉这个问题。。原因是这样,看了这篇文章
http://www.codeproject.com/KB/cpp/CppDelegateImplementation.aspx?fid=420921&fr=11#xx0xx
他实现了函数委托,但是他没有实现multicast, 具体描述见下


// 这个是delegate的基类
class delegate_root {

}
template <...>
class delegate0 : delegate_root { // 0个参数函数的委托
}
template <...>
class delegate1 : delegate_root { // 1个参数函数的委托
}
template <...>
class delegate2 : delegate_root { // 2个参数函数的委托
}


//使用的时候是这样
class A {
void funA(int x, int y) {
}
void funB(int x, int y) {
}
}

A a;
delegate2<void, int, int> d1(&a, &A::funA); //void是指 函数返回类型是void, 2个int是指委托函数有2个int参数
d1(100, 200); //delegate2重载了()操作符,这样就相当于调用 a.funA(100, 200)

delegate0<void, int, int> d2(&a, &A::funB); //同上, 又定义一个委托

// 所以想用一个vector容器来存放这些委托对象,由于delegate_root 是所有委托的基类,所以
vector<delegate_root*> vec;

// 这样问题就来了,delegate_root 必须有一个虚拟的 operator()
class delegate_root {
virtual operator() {
}
}

//但为了支持不同类型和个数的参数, 该函数必须是模板的,然后写上9遍..
class delegate_root {

template <> // 没参数的
virtual operator()() {
}
template <typename A1> // 一个参数的
virtual operator()(A1 a1) {
}
template <typename A1, typename A2> // 2个参数的
virtual operator()(A1 a1, A2 a2) {
}
}




这就导致了需要类似 模板成员函数 + 虚函数 的机制,,但写完后发觉这是c++不允许的。。

如何能改造整个结构 来达到同样目的?
谢谢!!!
...全文
607 点赞 收藏 20
写回复
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
aj3423 2010-08-13
额 用写9遍的方法解决了。。在调用的时候加个强制转换
class FunSet {
private:
public:
vector<delegate_root*> v;
typedef vector<delegate_root*>::iterator _it;

void add(delegate_root* d) {
v.push_back(d);
}
void call(){
for(_it it = v.begin(); it != v.end(); it++) {
(*(*it))();
}
}
template <typename A1, typename A2>
void call(A1 a1, A2 a2){
for(_it it = v.begin(); it != v.end(); it++) {
(*((delegate2<bool, A1, A2>*)(*it)))(a1, a2); //这里强制转换成delegate2<bool, A1, A2> ...
}
}
};

回复
aj3423 2010-08-12
[Quote=引用 11 楼 jameshooo 的回复:]

按我的阅读和设计经验,一个体系结构如果要用模板,那么这个体系里应该全都是模板,如果实在要公开某些虚基类,一般在模板继承链中最底层(最后派生层)实现基类函数(除非有特殊需求),但前提是这个虚基类的成员参数不能是模板。
[/Quote]
谢谢, 相当深刻。"前提是这个虚基类的成员参数不能是模板" 这就是因为虚函数不能模板化的原因吧。但如果9种参数的delegate_X分别从9种基类继承, 就没法用一种容器来装所有的委托对象了吧, 其实我的目标就是写一个Observer,用一个通用的容器来放各种委托对象,比如

class FunSet {// 用来存放各种委托
private:
public:
vector<delegate_root*> v;
typedef vector<delegate_root*>::iterator _it;

void add(delegate_root* d) {
v.push_back(d);
}
void call(){ // 0参数的
for(_it it = v.begin(); it != v.end(); it++) {
(*(*it))();
}
}
template <typename A1, typename A2>
void call(A1 a1, A2 a2){ // 2参数的
for(_it it = v.begin(); it != v.end(); it++) {
(*(*it))(a1, a2);
}
}
};

class Observable {
typedef map<int, FunSet>::iterator ob_it;
private:
public:
map<int, FunSet> m_obs;

void addListener(int msgType, delegate_root* d) {
ob_it it = m_obs.find(msgType); // 找当前msgType是否已有FunSet
if(it == m_obs.end()) {
m_obs[msgType] = FunSet(); //没有的话 创建一个
}
m_obs[msgType].add(d); //然后把 d 添加进去
}
bool fireEvent(int msgType) { // 0参数的广播
FunSet fs = (*(m_obs.find(msgType))).second;
fs.call();
}
template <typename A1, typename A2>
bool fireEvent(int msgType, A1 a1, A2 a2) { // 2参数的广播
FunSet fs = (*(m_obs.find(msgType))).second;
fs.call(a1, a2);
return true;
}
};

//用起来就比较方便了
class A {
public:
bool hello() { // 0参数的方法
cout << "hello" << endl;
return true;
}
bool resize(int x, int y) { // 2参数的方法
cout << "resize: x=" << x << ", y=" << y << endl;
return true;
}
};

#define REFRESH 1
#define RESIZE 2

int main() {
typedef delegate0<bool> WinHelloListener;
typedef delegate2<bool, int, int> WinResizeListener;

Observable window;

A a;

window.addListener(REFRESH, new WinHelloListener(&a, &A::hello));
window.fireEvent(REFRESH);// 这里能正确调用 A::hello ,因为无参数,所以不需要模板化,只需要函数是虚拟的就可以了

window.addListener(RESIZE, new WinResizeListener(&a, &A::resize));
window.fireEvent(RESIZE, 1, 2); // 但这里调用的却是 delegate_root::operator(x,y), 因为有参数,所以该函数需要模板化, 但这就不能是虚函数了,导致了子类delegate2 的operator(x,y)不能覆盖基类delegate_root的operator(x,y)

return 0;
}




回复
aj3423 2010-08-11
[Quote=引用 5 楼 cnzdgs 的回复:]
就是自己实现“虚函数”。用一个变量保存“虚函数”指针,构造函数中初始化该变量,调用函数时将该变量转换为相应的函数指针类型来调用。
[/Quote]
各子类里的方法的返回值,参数个数和类型都不相同,如果写个成员函数指针来指向各个子类里的方法,那在父类里如何声明这个指针? 比如
template <class RetType>
class delegate0 : delegate_root {
typedef RetType (delegate0::*fp)(PARAMS);
public:
fp m_fp;

delegate0(xxx) {
m_fp = delegate0::();//在delegate0的构造函数里把指针指向这个类的 operator()
}
}


class delegate_root {
template<class RetType, class A1, class A2>
RetType call(a1, a2) { // 用一个call函数来调用指针指向的函数
m_fp(a1, a2); // 但这里的m_fp 该怎么声明呢?

}
}


[Quote=引用 6 楼 jameshooo 的回复:]

用了模板再用虚函数就没有意思了,我很早就想公开自己的一段委托代码
[/Quote]

这个从使用上和链接里的很相似,也是用宏传9个参数进去调用9次生成不同的子类,不知道有没有实现多播的功能?



可能是我一开始在容器的使用上就错了,不能简单的用一个 vector<delegate_root*> vec 来放所有的委托对象
class Observer {
map<int, vector<delegate_root*>> _map;
add(int msgType, delegate_root* d) {
_map[msgType].push_back(d);
}
}
而是应该把add函数模板化
class Observer {
template <class T>
add(int T* d) {
if(T类型容器还没创建过) { //这种做法在c++里可否实现?
vecT = new vector<T>
}
vecT.push_back(d);
}
}
但这样的话就需要Observer里会动态创建各种类型的对象,这个在c++里可否实现?

回复
lisunlin0 2010-08-11
应该从设计的时候就避免模板和虚函数共存。
回复
zjz800800 2010-08-11
很好的一个话题
回复
用tr1::function 做委托多方便
回复
m_tornado 2010-08-11
赶来膜拜下~
回复
jameshooo 2010-08-11
按我的阅读和设计经验,一个体系结构如果要用模板,那么这个体系里应该全都是模板,如果实在要公开某些虚基类,一般在模板继承链中最底层(最后派生层)实现基类函数(除非有特殊需求),但前提是这个虚基类的成员参数不能是模板。

在模板世界里,虚函数除了增加虚表大小和降低运行性能,没有实际意义。STL/ATL/WTL是典型的全模板架构,没有任何虚函数,MFC采用的是全虚表架构,虽然有部分模板类,但只是作为工具类存在,有没有都不影响整体。

全模板架构的好处就是:你能由粗到细来设计模板。一旦某种粒度的模板不能满足要求,则可以把需要抽象的部分再次提炼成模板而不是提炼成基类,而且所有函数都是编译期就定好了。这样不断提炼不断细分的后果就是代码越来越晦涩难懂,可读性很差。全虚表架构刚好相反,你必须先设计基类,再实现派生类,它的代码可读性非常好,但虚表的值是在运行期才填入的。

废话说了不少,现在尝试解决你的问题。你不是想实现函数调用操作符吗,先按照参数个数实现9个模板类:
template<class T>
class CallT0
{
public:
void operator()(){}
};

template<class T, typename param1>
class CallT1
{
public:
void operator()(param1 p1) {...}
};

......

第二步,根据实际的类选择不同的模板基类:
template<class T>
class delegate_0 : public CallT0<T>
{
...
};

template<class T, typename param1>
class delegate_1 : public CallT1<T,param1>
{
...
};
......

你有没有发现这种设计很好写代码?强行把编译期的多态和运行期的多态混合在一起,就像新手练瑜伽,无论如何找不到着力点。
回复
windywater 2010-08-11
学习一下
回复
icefairy 2010-08-10
膜拜下LS几位大牛 mark
回复
dahaiI0 2010-08-10
学习下,再马克下
回复
jameshooo 2010-08-09
用了模板再用虚函数就没有意思了,我很早就想公开自己的一段委托代码,既能函数委托,也能类成员函数委托,只是一直没写介绍,现在贴出来供参考,没有用到公共基类,现在也懒得整理资料,随便看看吧。

// delegate.h

#pragma once


/*
* 以下宏用于预定义委托类的准备
*/
//#define pre_comma
#define comma ,
#define _V0(v)
#define _V1(v) v(p1)
#define _V2(v) v(p1) comma v(p2)
#define _V3(v) v(p1) comma v(p2) comma v(p3)
#define _V4(v) v(p1) comma v(p2) comma v(p3) comma v(p4)
#define _V5(v) v(p1) comma v(p2) comma v(p3) comma v(p4) comma v(p5)
#define _V6(v) v(p1) comma v(p2) comma v(p3) comma v(p4) comma v(p5) comma v(p6)
#define _V7(v) v(p1) comma v(p2) comma v(p3) comma v(p4) comma v(p5) comma v(p6) comma v(p7)
#define _V8(v) v(p1) comma v(p2) comma v(p3) comma v(p4) comma v(p5) comma v(p6) comma v(p7) comma v(p8)
#define _V9(v) v(p1) comma v(p2) comma v(p3) comma v(p4) comma v(p5) comma v(p6) comma v(p7) comma v(p8) comma v(p9)

#define _VN(para_cnt, v) _V##para_cnt(v)

#define semicolon ;
#define _VV0(v)
#define _VV1(v) v(p1)
#define _VV2(v) v(p1) semicolon v(p2)
#define _VV3(v) v(p1) semicolon v(p2) semicolon v(p3)
#define _VV4(v) v(p1) semicolon v(p2) semicolon v(p3) semicolon v(p4)
#define _VV5(v) v(p1) semicolon v(p2) semicolon v(p3) semicolon v(p4) semicolon v(p5)
#define _VV6(v) v(p1) semicolon v(p2) semicolon v(p3) semicolon v(p4) semicolon v(p5) semicolon v(p6)
#define _VV7(v) v(p1) semicolon v(p2) semicolon v(p3) semicolon v(p4) semicolon v(p5) semicolon v(p6) semicolon v(p7)
#define _VV8(v) v(p1) semicolon v(p2) semicolon v(p3) semicolon v(p4) semicolon v(p5) semicolon v(p6) semicolon v(p7) semicolon v(p8)
#define _VV9(v) v(p1) semicolon v(p2) semicolon v(p3) semicolon v(p4) semicolon v(p5) semicolon v(p6) semicolon v(p7) semicolon v(p8) semicolon v(p9)

#define _VVN(para_cnt, v) _VV##para_cnt(v)


#define _templist(para) typename para
#define _paralist(para) para
#define _paravarlist(para) para _##para
#define _savelist(para) para saved_##para
#define _savetolist(para) saved_##para = _##para
#define _restorelist(para) pThis->saved_##para
#define _callbacklist(para) va_arg(pThis->_vas, para)

#define DeclareDelegate(para_cnt) \
template<class T, typename retT _isZero _VN(para_cnt, _templist)> \
class Delegate##para_cnt \
{ \
typedef retT (T::*FnType)(_VN(para_cnt, _paralist)); \
typedef Delegate##para_cnt<T, retT _isZero _VN(para_cnt, _paralist)> theClass; \
public: \
T* _this; \
Raptor::Thread* _thread; \
FnType _fn; \
_VVN(para_cnt, _savelist); \
retT _ret; \
\
Delegate##para_cnt( FnType fn) : _fn(fn), _thread(0), _this(0) {} \
Delegate##para_cnt( FnType fn, Raptor::Thread* thread, T* obj) : _fn(fn), _thread(thread), _this(obj) {} \
\
retT operator()(Raptor::Thread* thread, T* obj _isZero _VN(para_cnt, _paravarlist)) \
{ \
if (obj==NULL || thread==NULL) return (retT)0; \
_this = obj; \
/*va_start(_vas, obj);*/ \
_VVN(para_cnt, _savetolist); \
thread->AddTaskAndWait(ThreadTask::New(_callback, this)); \
return _ret; \
} \
retT operator()(_VN(para_cnt, _paravarlist)) \
{ \
if (_this==NULL || _thread==NULL) return (retT)0; \
_VVN(para_cnt, _savetolist); \
_thread->AddTaskAndWait(ThreadTask::New(_callback, this)); \
return _ret; \
} \
static LONG _callback(LPVOID data) \
{ \
theClass * pThis = (theClass*)data; \
pThis->_ret = (pThis->_this->*pThis->_fn)(_VN(para_cnt, _restorelist)/*_VN(para_cnt, _callbacklist)*/); \
return 0; \
} \
}; \
template<typename retT _isZero _VN(para_cnt, _templist)> \
class DelegateApi##para_cnt \
{ \
typedef retT (WINAPI *FnType)(_VN(para_cnt, _paralist)); \
typedef DelegateApi##para_cnt<retT _isZero _VN(para_cnt, _paralist)> theClass; \
public: \
Raptor::Thread* _thread; \
FnType _fn; \
_VVN(para_cnt, _savelist); \
retT _ret; \
\
DelegateApi##para_cnt(FnType fn) : _fn(fn), _thread(0) {} \
DelegateApi##para_cnt(FnType fn, Raptor::Thread* thread) : _fn(fn), _thread(thread) {} \
\
retT operator()(Raptor::Thread* thread _isZero _VN(para_cnt, _paravarlist)) \
{ \
if (thread==NULL) return (retT)0; \
_VVN(para_cnt, _savetolist); \
thread->AddTaskAndWait(ThreadTask::New(_callback, this)); \
return _ret; \
} \
retT operator()( _VN(para_cnt, _paravarlist)) \
{ \
if (_thread==NULL) return (retT)0; \
_VVN(para_cnt, _savetolist); \
_thread->AddTaskAndWait(ThreadTask::New(_callback, this)); \
return _ret; \
} \
static LONG _callback(LPVOID data) \
{ \
theClass * pThis = (theClass*)data; \
pThis->_ret = pThis->_fn(_VN(para_cnt, _restorelist)); \
return 0; \
} \
}; \
/* 以下是返回类型为void时的特化模板 */ \
template<class T _isZero _VN(para_cnt, _templist)> \
class Delegate##para_cnt<T,void _isZero _VN(para_cnt, _templist)> \
{ \
typedef void (T::*FnType)(_VN(para_cnt, _paralist)); \
typedef Delegate##para_cnt<T, void _isZero _VN(para_cnt, _paralist)> theClass; \
public: \
Raptor::Thread* _thread; \
T* _this; \
FnType _fn; \
_VVN(para_cnt, _savelist); \
\
Delegate##para_cnt( FnType fn) : _fn(fn), _thread(0), _this(0) {} \
Delegate##para_cnt( FnType fn, Raptor::Thread* thread, T* obj) : _fn(fn), _thread(thread), _this(obj) {} \
\
void operator()(Raptor::Thread* thread, T* obj _isZero _VN(para_cnt, _paravarlist)) \
{ \
if (obj==NULL || thread==NULL) return; \
_this = obj; \
_VVN(para_cnt, _savetolist); \
thread->AddTaskAndWait(ThreadTask::New(_callback, this)); \
} \
void operator()( _VN(para_cnt, _paravarlist)) \
{ \
if (_this==NULL || _thread==NULL) return; \
_VVN(para_cnt, _savetolist); \
_thread->AddTaskAndWait(ThreadTask::New(_callback, this)); \
} \
static LONG _callback(LPVOID data) \
{ \
theClass * pThis = (theClass*)data; \
(pThis->_this->*pThis->_fn)(_VN(para_cnt, _restorelist)/*_VN(para_cnt, _callbacklist)*/); \
return 0; \
} \
}; \
template< _VN(para_cnt, _templist)> \
class DelegateApi##para_cnt<void _isZero _VN(para_cnt, _templist)> \
{ \
typedef void (WINAPI *FnType)(_VN(para_cnt, _paralist)); \
typedef DelegateApi##para_cnt<void _isZero _VN(para_cnt, _paralist)> theClass; \
public: \
Raptor::Thread* _thread; \
FnType _fn; \
_VVN(para_cnt, _savelist); \
\
DelegateApi##para_cnt(FnType fn) : _fn(fn), _thread(0) {} \
DelegateApi##para_cnt(FnType fn, Raptor::Thread* thread) : _fn(fn), _thread(thread) {} \
\
void operator()(Raptor::Thread* thread _isZero _VN(para_cnt, _paravarlist)) \
{ \
if (thread==NULL) return; \
_VVN(para_cnt, _savetolist); \
thread->AddTaskAndWait(ThreadTask::New(_callback, this)); \
} \
void operator()( _VN(para_cnt, _paravarlist)) \
{ \
if (_thread==NULL) return ; \
_VVN(para_cnt, _savetolist); \
_thread->AddTaskAndWait(ThreadTask::New(_callback, this)); \
} \
static LONG _callback(LPVOID data) \
{ \
theClass * pThis = (theClass*)data; \
pThis->_fn(_VN(para_cnt, _restorelist)); \
return 0; \
} \
};

//////////////////////////////////////////////////////////////////////////
// 委托类准备结束
//////////////////////////////////////////////////////////////////////////


#define Main_Thread() Raptor::Thread::MainThread()
#define HWND2Thread(hwnd) Raptor::Thread::FromHandle(hwnd)

//////////////////////////////////////////////////////////////////////////
/*
* 以下宏为ATL::CWindow的方法委托工具
*/

#define Dele_CWindow_DestroyWindow (Raptor::Delegate0<ATL::CWindow,BOOL>(&ATL::CWindow::DestroyWindow))
#define Dele_CWindow_SetFocus (Raptor::Delegate0<ATL::CWindow,HWND>(&ATL::CWindow::SetFocus))

// ATL::CWindow 委托工具宏结束
//////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////
/*
* 以下宏为 Window API 的函数委托工具
*/

#define DeleApi_SetFocus (Raptor::DelegateApi1<HWND,HWND>(&::SetFocus))
#define DeleApi_ShowWindow (Raptor::DelegateApi2<BOOL,HWND,int>(&::ShowWindow))
#define DeleApi_DestroyWindow (Raptor::DelegateApi1<BOOL,HWND>(&::DestroyWindow))
#define DeleApi_SetWindowPos (Raptor::DelegateApi7<BOOL,HWND,HWND,int,int,int,int,UINT>(&::SetWindowPos))

// API 委托结束
//////////////////////////////////////////////////////////////////////////


在另外一个头文件中真正定义了模板类,最多9个参数

// class Delegate0<T, retT> and DelegateApi0<retT>
#define _isZero
DeclareDelegate(0)
#undef _isZero

// class Delegate1<T, retT, p1> and DelegateApi1<retT, p1>
#define _isZero ,
DeclareDelegate(1)

// class Delegate2<T, retT, p1, p2> and DelegateApi2<retT, p1, p2>
DeclareDelegate(2)

// class Delegate3<...> and DelegateApi3<...>
DeclareDelegate(3)

// class Delegate4<...> and DelegateApi4<...>
DeclareDelegate(4)

// class Delegate5<...> and DelegateApi5<...>
DeclareDelegate(5)

// class Delegate6<...> and DelegateApi6<...>
DeclareDelegate(6)

// class Delegate7<...> and DelegateApi7<...>
DeclareDelegate(7)

// class Delegate8<...> and DelegateApi8<...>
DeclareDelegate(8)

// class Delegate9<...> and DelegateApi9<...>
DeclareDelegate(9)

回复
cnzdgs 2010-08-09
就是自己实现“虚函数”。用一个变量保存“虚函数”指针,构造函数中初始化该变量,调用函数时将该变量转换为相应的函数指针类型来调用。
回复
aj3423 2010-08-09
[Quote=引用 3 楼 cnzdgs 的回复:]
定义一个成员变量保存函数指针,使用时强制转换成相应的函数指针类型。
[/Quote]
能否再具体一些 -_-
回复
cnzdgs 2010-08-09
定义一个成员变量保存函数指针,使用时强制转换成相应的函数指针类型。
回复
羽飞 2010-08-09
模板、虚函数……,不会,顶一顶
回复
发动态
发帖子
VC/MFC
创建于2007-09-28

1.5w+

社区成员

VC/MFC相关问题讨论
申请成为版主
社区公告
暂无公告