简体   繁体   English

指针到成员功能的抽象:安全性和替代方案

[英]Abstracting pointer-to-member-function: safety and alternatives

In this problem, assume that we have handled all pointers in a nice, careful manner - to prevent question bloat I don't want to include my cleanup code here! 在这个问题中,假设我们已经以一种很好,谨慎的方式处理了所有指针-为防止问题question肿,我不想在这里包括我的清理代码!

Let's say we have two classes, Foo and Bar with class definitions as follows: 假设我们有两个类, FooBar ,其类定义如下:

class Foo
{
    public:
        Foo();
        void fooFn();
};

class Bar
{
    public:
        Bar();
        void barFn();
};

Assume that it is necessary that Foo and Bar have no inheritance relationship, but we need to call both fooFn and barFn in response to some stimulus. 假定FooBar没有继承关系是必要的,但是为了响应某些刺激,我们需要同时调用fooFnbarFn We can create a controller class with a container from which to call fooFn and barFn on specific instances of Foo and Bar - a static std::vector for example - but we run into an issue: pointers to member functions of Foo and Bar instances are of different types. 我们可以创建一个带有容器的控制器类,从中可以在FooBar特定实例上调用fooFnbarFn例如,一个静态std::vector barFn但遇到一个问题:指向FooBar实例的成员函数的指针是不同的类型。

By using a static vector< std::function<void()>* > in the controller class, we can make a workaround. 通过在控制器类中使用静态vector< std::function<void()>* > ,我们可以进行解决。 Foo and Bar instances can have a function which adds pointers to the vector through a lambda function which captures this : FooBar实例可以具有一个函数,该函数通过捕获this的lambda函数添加指向矢量的指针:

void Foo::registerFnPointer()
{
    ControllerClass::function_vector.push_back( new [this](){ return this->fooFn(); } );
}

I have tested this method, and it appears to work without any problems. 我已经测试了这种方法,它似乎可以正常工作。 That said, I am concerned about the issues that could be caused by circumventing the type difference mentioned before... Am I worrying over nothing? 就是说,我担心因规避前面提到的类型差异而可能引起的问题……我是否在担心什么? Is there a better way to accomplish equivalent functionality? 有没有更好的方法来完成等效功能?

The only problem I see has actually nothing to do with the functors but has to do with object lifetime. 我看到的唯一问题实际上与函子无关,但与对象生存期有关。 That is: I'm not sure how you ensure that you always de-register the functors registered with ControllerClass whenever an Foo or Bar instance gets destroyed. 也就是说:我不确定如何确保在Foo或Bar实例被销毁时始终注销在ControllerClass中注册的函子。

You mention however that you do proper memory management. 但是,您提到要进行适当的内存管理。

In my opinion you do not need to store a pointer to function<void()> , you can simply store function as value (that is have a vector<function<void()>> ). 在我看来,您不需要存储指向function<void()>的指针,您可以简单地将function存储为值(即具有vector<function<void()>> )。

Prior to C++11 and lambdas, to achieve the same effect you would have used a (boost) function also but you would would have used boost::bind with with the address of the fooFn and the first parameter bound to a pointer (or reference) to the Foo object instance. 在C ++ 11和lambdas之前,要获得相同的效果,您还可以使用(boost)函数,但是您应该使用boost :: bind与fooFn的地址以及第一个绑定到指针的参数(或引用)到Foo对象实例。

This would have created an instance of the function that holds all of the information needed to call the fooFn method on the given object. 这将创建该函数的实例,该实例包含在给定对象上调用fooFn方法所需的所有信息。 You could then store the instance in a vector to call it at a later time (and had the same problem of making sure no boost::function bound to a destroyed object remains registered) 然后,您可以将实例存储在向量中以在以后调用它(并且存在确保未绑定到被破坏对象的boost::function仍然存在的相同问题)

Edit: 编辑:

For the sake of completeness, the link to the Boost bind documentation specific for binding members: http://www.boost.org/doc/libs/1_56_0/libs/bind/bind.html#with_member_pointers 为了完整起见,特定于绑定成员的Boost绑定文档的链接: http : //www.boost.org/doc/libs/1_56_0/libs/bind/bind.html#with_member_pointers

What you are doing is actually quite similar only that you are now using a lambda to capture the object pointer and to define the function to be called. 实际上,您所做的实际上非常相似,只是您现在使用lambda来捕获对象指针并定义要调用的函数。

So I see no problem with what you are doing (other then the one I already mentioned). 因此,我认为您的工作没有问题(除了我已经提到的问题之外)。

You could use an adapter class. 您可以使用适配器类。 This might be overkill for what you're doing, but it may work. 对于您正在做的事情,这可能是过大的,但它可能会起作用。

The benefits of doing it this way are: 这样做的好处是:

  1. You don't have to change the original classes. 您不必更改原始类。 Creating void Foo::registerFnPointer() is ugly. 创建void Foo::registerFnPointer()很丑。

  2. You don't have to use your static std::vector . 您不必使用静态std::vector

  3. You don't have to deal with function pointers. 您不必处理函数指针。

So let's say you have two different classes like this: 因此,假设您有两个不同的类,例如:

struct Foo
{
    void fooFn () { 
        std::cout << "Foo::fooFn ()" "\n" ; 
    }
};

struct Bar
{
    void barFn () {
        std::cout << "Bar::barFn ()" "\n" ; 
    }
};

The goal is to put them into a container and call their respective *Fn () member-functions. 目标是将它们放入容器中并调用它们各自的*Fn ()成员函数。

An adapter would look something like this: 适配器看起来像这样:

struct Adapter_Base
{
    virtual ~Adapter_Base () {} ;

    virtual void adapterFn () = 0 ;
};

template <typename T>
struct Adapter : Adapter_Base
{
    T tVal ;

    Adapter (const T &tVal) : tVal (tVal) {}
    void adapterFn () ;
};

template <>
void Adapter <Foo>::adapterFn ()
{
    tVal.fooFn () ;
}

template <>
void Adapter <Bar>::adapterFn ()
{
    tVal.barFn () ;
}

And you could use it like this: 您可以这样使用它:

int main ()
{
    std::vector <std::unique_ptr <Adapter_Base> > v1 ;

    std::unique_ptr <Adapter_Base> u1 (new Adapter <Foo> (Foo ())) ;
    std::unique_ptr <Adapter_Base> u2 (new Adapter <Bar> (Bar ())) ;

    v1.push_back (std::move (u1)) ;
    v1.push_back (std::move (u2)) ;

    for (auto &adapter : v1) {
        adapter->adapterFn () ;
    }

    return 0 ;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM