在C ++中获取指向调用对象的指针

时间:2014-05-30 22:07:25

标签: c++ c++11 member-function-pointers

我的问题相当简单:如何在不传递this的情况下获取指向调用函数的对象的指针?

然而,我要问的原因稍微复杂一些。

我被问到这是因为我正在编写一个类(名为SubordnateRegistry),它包含一个函数委托的异构列表(列表) - 具有不同签名的可调用对象。

无论哪个类希望有代表调用,都必须注册自己,当然,还要注册他们希望接收的数据类型,以及指向对象的指针或已经构造的委托。来自类Bar内部的调用可能如下所示:

ARegistry.Subscribe <Foo> (& Bar:: BarFunction, this);

或者像这样:

using namespace std:: placeholders;
auto Func = std:: bind (& Bar:: BarFunction, this, _1);
ARegistry.Subscribe <Foo> (Func);

因为std:: function更通用(你可以绑定任何可调用的,包括lambdas),我宁愿在原始指针上使用它。

我的问题在于我必须始终指定this。如果我传递了一个无效指针,或者最终注册了一个完全不相关的对象,那么我将来会在某个不确定的位置调用UB而不知道它来自哪里或错误的根源在哪里。讨厌!

现在,如果你想知道如果调用者不是一个对象(一个自由函数)会发生什么,那么该元素将永远保持注册状态,因为我永远不会自动删除(取消注册)可调用对象,因为它永远不会破坏。

任何想法,或者这是不可能的,因为我被引导相信?预处理器魔术是一种选择吗?有没有更好的办法?

当然,欢迎任何问题。谢谢你的时间 :)。

2 个答案:

答案 0 :(得分:1)

例如,您可以使用#define自动包含this

#define REG_SUBSCRIBE(t,f) ARegistry.Subscribe <t> (f, this)

当然,这可能会变得很难看,所以我不知道我是否会推荐它。

如果您担心跟踪并释放它们,那么我建议使用析构函数返回一个取消订阅该对象的对象。或者(或另外)提供方法调用来执行此操作。

答案 1 :(得分:1)

我使用奇怪的重复模板模式给了它一个镜头。我们的想法是将一个方法的注册封装在一个类中,该类的析构函数将在销毁时自动注销客户端对象。客户端派生自此类。这样,析构函数将自动调用,注册类具有对客户端this指针的隐式访问权限。因此没有必要明确地通过它。

访客类

在这个答案中,我将假设以下访客类:

// The visitor class. Clients can register methods here.
class Visitor {
  public:
    // Expects void(void) methods.
    template <class T>
    using method_t = void (T::*)();

    // Register a new method of a client.
    template <class T>
    void subscribe(method_t<T> method, T* object);

    // unsubscribe all methods of a given client.
    template <class T>
    void unsubscribe(T* object);
};

方法的注册

注册类如下所示:

// CRTP registration class.
// Will automatically subscribe and unsubscribe its base class.
template <class Derived>
class Registration {
  public:
    // The constructor takes a reference to the visitor,
    // and a pointer to the method that should be registered.
    // It will then register this particular method.
    Registration(Visitor &visitor, Visitor::method_t<Derived> method)
        : visitor_(visitor) {
        visitor_.subscribe(method, static_cast<Derived *>(this));
    }

    // The destructor calls the visitors unsubscribe method unregistering
    // all methods of this particular client.
    virtual ~Registration() {
        visitor_.unsubscribe(static_cast<Derived *>(this));
    }

  private:
    Visitor &visitor_;
};

客户

实际的客户端看起来像这样。

// A client using the registration class.
// Has to inherit publically, otherwise the `this` pointer cast will fail.
class Client : public Registration<Client> {
  public:
    Client(Visitor &visitor)
        : Registration<Client>(visitor, &Client::method) {}

    void method();
};

您可以找到一个示例用法here

一些注释:

  • 由于注册类具有非平凡的析构函数,因此需要考虑五的规则,并需要决定如何处理copy- / move-构造函数/赋值。
  • 如果要注册一个对象的多个方法,则需要更改此模式。考虑一下如何解决问题的粗略草图。 Registration类可以在公共可用的成员函数中注册方法而不是构造函数。如果由于某种原因,您需要多次从Registration继承,请考虑将对访问者和基类链的引用分解出来。
  • 如果取消订阅方法也需要那个,那么注册也可以存储方法指针。