ACG狂人

其实我更爱姐汁...

boost::bind绑定成员函数时,第一个参数传递对象的特殊情况

boost::bind(&memberfunction, obj, _1, _2........)类似这样的用法,我们叫做成员函数绑定,boost库的文档中说的很清楚,第一个参数可以是value、pointer和reference,即传值、传地址和传引用都是可以的,所以在一般情况下,下面三种使用bind的形式都是成立的。

class A
{
public:
    
void func();
}
;


A a;
A
& r = a;

boost::bind(
&A::func, a);
boost::bind(
&a::func, &a);
boost::bind(
&a::func, r);

由上面的代码可以看出,我们可以随便传任意一种类对象的形式,函数模板会自动寻找最为匹配的为我们实现。但是有两种情况是特殊的,即:
1、该对象不可进行拷贝构造函数。
2、该对象不可随意被析构。
发现这个问题是在我编写单件模式时的遇见的,当时发现我的单件对象在bind中被析构了一次,这很不寻常,为什么bind会调用第一个参数的析构呢?跟踪进了boost的源码才发现,原来所有的参数都会被拷贝一遍,然后析构一遍,这样一来,我们传递参数的时候就会有一些小麻烦了,首先必须保证参数能够被拷贝而不影响逻辑和数据一致性,其次,参数能够被析构而不影响逻辑和数据一致性。单件是全局性质的数据,所以绝对不可以析构,那么这种情况的话,我们只好传递单件对象的地址,而不能传递值或引用。

另:附上出错问题的代码如下
class InputDevice
    : 
public EventSource
    , 
public Singleton<InputDevice>
{
public:
    
}
;

class TestUI
    : 
public Singleton<TestUI>
{
public:
    
~TestUI(){
        std::cout
<<"~TestUI"<<std::endl;
    }

    
void processKeyboard(EventArgs& args){
        std::cout
<<"键盘响应"<<std::endl;
    }


    
void processMouse(EventArgs& args){
        std::cout
<<"鼠标响应"<<std::endl;
    }

}
;


int _tmain(int argc, _TCHAR* argv[])
{
    
new FrameUpdaterManager;
    
new DelayEventSender;
    
new InputDevice;
    
new TestUI;

    InputDevice::getSingleton().mEventSet.addEvent(
"KeyDown", Event());
    InputDevice::getSingleton().mEventSet.addEvent(
"KeyUp", Event());
    InputDevice::getSingleton().mEventSet.addEvent(
"MouseLDown", Event());
    InputDevice::getSingleton().mEventSet.addEvent(
"MouseLUp", Event());
    InputDevice::getSingleton().mEventSet.addEvent(
"MouseRDown", Event());
    InputDevice::getSingleton().mEventSet.addEvent(
"MouseRUp", Event());


    
//TestUI& ui = TestUI::getSingleton(); // 用此行便会出错
    TestUI* ui = TestUI::getSingletonPtr();

    
// 出错开始
    InputDevice::getSingleton().mEventSet["KeyDown"+= boost::bind(&TestUI::processKeyboard, ui, _1);
    InputDevice::getSingleton().mEventSet[
"KeyUp"+= boost::bind(&TestUI::processKeyboard, ui, _1);

    InputDevice::getSingleton().mEventSet[
"MouseLDown"+= boost::bind(&TestUI::processMouse, ui, _1);
    InputDevice::getSingleton().mEventSet[
"MouseLUp"+= boost::bind(&TestUI::processMouse, ui, _1);
    InputDevice::getSingleton().mEventSet[
"MouseRDown"+= boost::bind(&TestUI::processMouse, ui, _1);
    InputDevice::getSingleton().mEventSet[
"MouseRUp"+= boost::bind(&TestUI::processMouse, ui, _1);


    delete TestUI::getSingletonPtr();
    delete InputDevice::getSingletonPtr();
    delete DelayEventSender::getSingletonPtr();
    delete FrameUpdaterManager::getSingletonPtr();
    
return 0;
}

posted on 2009-06-15 22:34 酿妹汁 阅读(5671) 评论(4)  编辑 收藏 引用 所属分类: C++

评论

# re: boost::bind绑定成员函数时,第一个参数传递对象的特殊情况 2009-06-16 00:14 一个无聊的人

为啥不用boost::mem_fn ?  回复  更多评论   

# re: boost::bind绑定成员函数时,第一个参数传递对象的特殊情况 2009-06-16 08:27 董波

6.2. Requirements for Call Wrapper Types
TR1 defines some additional terms that are used to describe requirements for callable types.

First, INVOKE(fn, t1, t2, ..., tN) describes the effect of calling a callable object fn with the arguments t1, t2, ..., tN. Naturally, the effect depends on the type of the callable object. INVOKE is defined as follows:

(t1.*fn)(t2, ..., tN) when fn is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T

((*t1).*fn)(t2, ..., tN) when fn is a pointer to a member function of a class T and t1 is not one of the types described in the previous item

t1.*fn when fn is a pointer to member data of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T

(*t1).*fn when fn is a pointer to member data of a class T and t1 is not one of the types described in the previous item

fn(t1, t2, ..., tN) in all other cases

What this amounts to is that when the callable object is an ordinary function or a pointer to an ordinary function, INVOKE means to call that function, passing the rest of the arguments to the function call. When the callable object is a pointer to member, the next argument refers to the object that it should be applied to. That argument is the object itself, a reference to the object, a pointer to the object, or some kind of smart pointer that points to the object. The rest of the arguments are passed to the function call.

Second, INVOKE_R(fn, t1, t2, ..., tN, Ret) describes the effect of calling a callable object fn with an explicit return type, Ret. It is defined as INVOKE(fn, t1, t2, ..., tN) implicitly converted to Ret.[5]

[5] In the TR, this metafunction is named INVOKE; although I'm one of the people responsible for this name overloading, I've now concluded that it's too clever and shouldn't be used.

Third, some call wrapper types have a weak result type; this means that they have a nested member named result_type that names a type determined from the call wrapper's target type, Ty.

If Ty is a function, reference to function, pointer to function, or pointer to member function, result_type is a synonym for the return type of Ty

If Ty is a class type with a member type named result_type, result_type is a synonym for Ty::result_type

Otherwise, result_type is not defined[6]

[6] That is, not defined as a consequence of having a weak result type. Some call wrapper types have a weak result type in certain circumstances, have a specific type named result_type

A few examples will help clarify what this rather dense text means:

struct base {
void f();
int g(double);
int h(double,double);
};
struct derived : base {
};

base b;
derived d;
base& br = d;



With these definitions, rule 1 gives the following meanings to these uses of INVOKE .

Phrase
Meaning

INVOKE (&base::f, b)
(b.*f)()

INVOKE (&base::g, d, 1.0)
(d.*f)(1.0)

INVOKE (&base::h, br, 1.0, 2.0)
(br.*f)(1.0, 2.0)





That is, the pointer to member function is called on the object or reference named by t1:

derived *dp = new derived;
base *bp = dp;
shared_ptr<base> sp(bp);



With these additional definitions, rule 2 gives the following meanings to these uses of ( INVOKE):

Phrase
Meaning

INVOKE (&base::f, bp)
((*bp).*f)()

INVOKE (&base::g, dp, 1.0)
((*dp).*f)(1.0)

INVOKE (&base::h, sp, 1.0, 2.0)
((*sp).*f)(1.0, 2.0)





That is, the pointer to member function is called on the object that the argument t1 points to. Since it uniformly dereferences that argument, the rule works for any type whose operator* returns a reference to a suitable object. In particular, the rule works for shared_ptr objects.

Rules 3 and 4 give similar meanings to INVOKE uses that apply pointers to member data:

void func(base&);
struct fun_obj {
void operator()() const;
bool operator()(int) const;
};
fun_obj obj;



With these additional definitions, rule 5 gives the following meanings to these uses of INVOKE:

Phrase
Meaning

INVOKE (func, d)
func(d)

INVOKE (obj)
obj()

INVOKE (obj, 3)
obj(3)


  回复  更多评论   

# re: boost::bind绑定成员函数时,第一个参数传递对象的特殊情况 2009-06-16 23:25 wp

文档上有说明  回复  更多评论   

# re: boost::bind绑定成员函数时,第一个参数传递对象的特殊情况 2009-06-19 14:49

哎!我没耐心看完文档,有点操之过急了,呵呵,得接受这个教训  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理