【问题标题】:C++: Store pointer to a member function of an object in another objectC ++:将指向另一个对象的对象的成员函数的指针存储在另一个对象中
【发布时间】:2015-06-16 13:24:25
【问题描述】:

我有一个类会在某些情况下调用用户指定的函数。因此,该类有一个方法void setExternalPostPaintFunction(void(*function)(QPainter&));,可用于“注册”一个函数。这个函数会在那个时候被调用:

class A {
    public:
        void setExternalPostPaintFunction(void(*function)(QPainter&));
    private:
        void (*_externalPostPaint)(QPainter&);
        bool _externalPostPaintFunctionAssigned;
};

函数指针保存在成员变量_externalPostPaint中。 setExternalPostPaintFunction 的实现如下所示:

void A::setExternalPostPaintFunction(void(*function)(QPainter&)) {
    _externalPostPaint = function;
    _externalPostPaintFunctionAssigned = true;
}

现在,这适用于正常功能。但是,我还希望能够将指针传递给对象的成员函数。据我所知,在这种情况下,我还必须传递并存储指向对象的指针。但是,我不知道另一个对象将具有哪种类型。所以我想我不得不使用模板。我已经想到了这样的事情:

class A {
    public:
        template <typename T>
        void setExternalPostPaintFunction(void(T::*function)(QPainter&), T* object);
    private:
        void (T::*_externalPostPaint)(QPainter&);       //<- This can't work!
        bool _externalPostPaintFunctionAssigned;
};

这样我可以将函数指针和对象指针传递给setExternalPostPaintFunction,并且可能能够在该函数内的对象上调用该函数。但是我无法将它存储在变量 _externalPostPaint 中,因为类型 T 仅在调用函数 setExternalPostPaintFunction 时推导出来,因此我不能拥有依赖于此类型的成员变量,因为在创建对象时必须知道我的成员变量的类型,除此之外它不能更改,但在分配可能是不同类型对象的成员函数的新函数的情况下,它必须知道。

那么正确的方法是什么,或者有什么方法吗?我不太适合模板和函数指针,所以我可能忽略了一些东西。

另一个选择当然是创建一个带有虚拟成员函数的仿函数类,该函数可以在派生类中被覆盖,然后传递 + 存储该类型的对象指针而不是函数指针。但如果可能的话,我会更喜欢我的方法。

编辑:解决方案

TartanLlama 建议使用std::function,让我走上了正轨。以下是我的解决方法:

class A {
    public:
        template <typename T>
        void setExternalPostPaintFunction(T* object, void(T::*function)(QPainter&)) {
            _externalPostPaint = std::bind(function, object, std::placeholders::_1);
            _externalPostPaintFunctionAssigned = true;
        }
        void setExternalPostPaintFunction(std::function<void(QPainter&)> const& function);
    private:
        std::function<void(QPainter&)> _externalPostPaint;
        bool _externalPostPaintFunctionAssigned;
};

如您所见,指向函数/成员函数的指针现在存储在std::function&lt;void(QPainter&amp;)&gt; 对象中。优点是,std::function 基本上可以存储任何可调用目标。然后有两个重载:一个可用于任何std::function 对象,它也接受例如一个普通的函数指针(因为预期的 std::function 是从该指针隐式构造的)和一个用于必须在对象上调用的成员函数(为了方便起见更多)。后者作为模板实现。这使用std::bind 在对象(用户传递)上创建该成员函数(用户传递)调用的std::function 对象。

采用std::function 的重载在源文件中实现如下:

void ImageView::setExternalPostPaintFunction(std::function<void(QPainter&)> const& function) {
    _externalPostPaint = function;
    _externalPostPaintFunctionAssigned = true;
}

A 类的代码中调用该存储函数现在就这么简单:

//canvas is a QPainter instance    
if (_externalPostPaintFunctionAssigned) _externalPostPaint(canvas);

想要将成员函数注册为回调函数的用户只需执行以下操作:

//_imageView is an instance of "A"
//"MainInterface" is the type of "this"
_imageView->setExternalPostPaintFunction(this, &MainInterface::infoPaintFunction);

或者如果它不是成员函数而只是普通函数:

void someFunction(QPainter& painter) {
    //do stuff
}

_imageView->setExternalPostPaintFunction(&someFunction);

或者他可以显式创建一个std::function对象并传递它:

std::function<void(QPainter&)> function = [&](QPainter& painter){ this->infoPaintFunction(painter); };
_imageView->setExternalPostPaintFunction(function);

像魅力一样工作。

【问题讨论】:

  • C++11 好吗? std::function 非常适合这种事情。
  • 是的,C++ 11 没问题。我去看看。

标签: c++ templates function-pointers member-functions


【解决方案1】:

你可以使用std::function:

class A {
    public:
        //PostPaintFun can be anything which acts as a function taking a QPainter&
        //Could be a lambda, function pointer, functor, etc.
        using PostPaintFun = std::function<void(QPainter&)>;
        void setExternalPostPaintFunction(PostPaintFun fun);
    private:
        //Names beginning with an underscore are reserved, don't use them
        //Ending with an underscore is fine
        PostPaintFun fun_;
        bool externalPostPaintFunctionAssigned_;
};

现在你可以像这样使用成员函数了:

struct B
{
    void exec(QPainter&) const;
};

void foo() {
    B b;
    a.setExternalPostPaintFunction(
        [b] (QPainter& p) {b.exec(p);}
    );
}

//or inside B
void B::foo() {
    a.setExternalPostPaintFunction(
        [this] (QPainter&p) {this->exec(p);}
    );
 }

【讨论】:

  • 感谢“std::function”的提示。您使用 lambda 的解决方案非常优雅。但是,我解决的方法略有不同,因为我认为这对班级的用户来说更容易一些。我的方式是他不必自己创建那个 lambda,而只需传递一个指向他的对象的指针和一个指向成员函数的指针,我的函数 setExternalPostPaintFunction 使用 std::bind 完成其余的工作。我编辑了我的问题并添加了我的解决方案。也许你可以看看它并告诉我你的想法。
  • 酷,这是一个相当不错的解决方案,但会阻止用户传入 lambda 或仿函数。我建议有一个构造函数,它采用std::function,然后一个采用指向成员函数的指针和一个实例指针,以方便使用。
  • 好的,已经想到了。想我会添加另一个直接采用std::function 的重载。
  • 啊,好吧,我刚刚意识到我现在可以去掉我添加的第二个重载(对于函数指针),因为 void setExternalPostPaintFunction(std::function&lt;void(QPainter&amp;)&gt; const&amp; function); 也将除了一个函数指针,通过 std::function 的构造函数是隐式转换为 std::function 对象。还有一个问题:你为什么要这么做using PostPaintFun = std::function&lt;void(QPainter&amp;)&gt;;
  • 这样我就不必输入两次了。 DRY 原则。
【解决方案2】:

我不得不说我更喜欢 TartanLlama 的回答,但这里有一些对你有用的东西。

这可能需要一些工作,但我相信你会明白的。

struct IFunctionHolder {};                      // Used for pointing to any FunctionHolder
typedef IFunctionHolder* functionHolder_ptr;    // Alias for IFunctionHolder* .

template<typename Function>                     // The template for the actual function holders.
struct FunctionHolder:  public IFunctionHolder
{
    Function function;
};


class A {
    public:
        template <typename T>
        void setExternalPostPaintFunction(void(T::*function)(QPainter&), T* object);
    private:
        functionHolder_ptr *function_holder;            // This memeber can hold eny instantiation of template<> FunctionHolder.
                                                        // Instantiate this member wen calling setExternalPostPaintFunction
        bool _externalPostPaintFunctionAssigned;
};

你可以有一些这样的代码:

A some_a;
void some_a.setExternalPostPaintFunction(&SomeInstance::some_fnunction);    // Here take place the instantiation of FunctionHolder.
some_a.function_holder.function(some_painter);

【讨论】:

  • 我决定采用std::function 方法。我在问题中添加了我的解决方案。
猜你喜欢
  • 1970-01-01
  • 2023-01-19
  • 1970-01-01
  • 2011-05-17
  • 2015-08-07
  • 1970-01-01
  • 2018-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多