如何分配子类的方法成员指针?

时间:2012-11-25 17:29:45

标签: c++ templates subclass member-function-pointers mem-fun

我的问题有点复杂。 我有一个类(e:Component)有Ports对象。 当Component创建一个Port对象时,它会将其中一个方法传递给Port构造函数。

方法签名:

typedef std::vector<std::string> (Component::*ComponentMethod)(std::vector<std::string>);

如果我这样做,它可以正常工作:

// in class Component

std::vector<std::string> Component::test_method( std::vector<std::string> ) {
    std::cout << "Hello !\n";
    // Do stuff
}

// Port ctor : Port( ComponentMethod callback );
Port* p = new Port(&Component::test_method);

嗯......现在,我的问题是我创建了类Component的子类,我不知道如何将子语句方法传递给一个端口。

// in class SubtypeComponent

std::vector<std::string> SubtypeComponent::test_method2( std::vector<std::string> ) {
    std::cout << "Hello !\n";
    // Do stuff
}

// Port ctor : Port( ComponentMethod callback );
Port* p = new Port(&SubtypeComponent::test_method2);
// ERROR :'(

看起来很正常:我猜编译器需要精确的Component(only)方法。

- &GT;我正在寻找一种“动态”分配端口方法的解决方案(但我不认为这是可能的)

- &GT;也许另一种解决方案应该是使用模板? (定义“模板方法指针”而不是“组件方法指针”),但我不确定。

任何帮助将不胜感激:)

4 个答案:

答案 0 :(得分:2)

指针到成员的转换起初有点反直觉。引用基本成员的指向成员的指针可以隐式转换为指向成员的指针到派生类型(反向方差),但反之则不然。

如果你真的考虑过它,它确实有意义:如果你有一个指向base成员的指针,那么它可以应用于派生类型,因为它保证有一个基础子对象。相反,指向派生类型的成员的指针可能指向基类型中不存在的成员,因此转换没有意义。

这是您必须解决的设计中的问题,如果您希望保持指向基类型的成员指针,指针必须引用基础中存在的成员(在在哪种情况下,表达式&derived::member的类型为T base::*,并且不需要转换),并且它们之前不得存储在派生类型的成员指针中(在存储它之后,编译器)无法知道指针是指引用的成员还是派生成员

答案 1 :(得分:1)

您显然无法将指向派生类型成员的指针转换为指向基类成员的指针:这将允许在基础对象上调用派生类型的成员而不会破坏类型系统!如果有的话,转换应该以相反的方式工作,即,您可能能够将指向基于成员的指针转换为指向派生类型成员的指针(但是,我这也是不可能的,但我不确定。)

完全绕过这个问题的方法是使用类型擦除的函数类型来传递。例如,您可以使用std::function<std::vector<std::string>(std::vector<std::string>)并将其设置为引用您的对象。如果你真的需要传入不同的对象作为参数,你可以将指针或引用传递给基类并将dynamic_cast<D&>(r)传递给期望的类型(如果它被错误的对象调用则处理异常)

答案 2 :(得分:0)

最简单的方法可能就是让Component使用一个名为test_method的虚拟方法。然后你可以将指向Component的指针传递给Port的构造函数,然后Port就可以调用这个虚方法:

Component* c = new Component();
// Port ctor : Port( Component* component );
Port* p = new Port(c); // Port would then call c->test_method();

SubtypeComponent* sc = new SubtypeComponent();
// Port ctor : Port( Component* component );
Port* p = new Port(sc); // Port would then call sc->test_method();

但是,如果由于某种原因这不能给你足够的灵活性,还有另一种方法来解决你的问题:你需要传递一个类型而不是方法指针。然后,此类型保存方法(和对象,因为您无法在没有对象的情况下调用方法)。以下是此类型的示例(如果您可以使用C ++ 11,也可以使用std :: function):

class Handler {
    public:
    virtual std::vector<std::string> handle (std::vector<std::string> arguments) = 0;
};

template <class C> class Method: public Handler {
    C* object;
    std::vector<std::string> (C::*method) (std::vector<std::string>);
    public:
    Method (C *object, std::vector<std::string>(C::*method)(std::vector<std::string>)) {
        this->object = object;
        this->method = method;
    }
    virtual std::vector<std::string> handle (std::vector<std::string> arguments) {
        // call the method
        return (object->*method) (arguments);
    }
};

现在,您修改Port类构造函数以将指向Handler的指针作为参数。然后你可以写:

Component* c = new Component();
// Port ctor : Port( Handler* handler );
Port* p = new Port(new Method<Component>(c, &Component::test_method));

SubtypeComponent* sc = new SubtypeComponent();
// Port ctor : Port( Handler* handler );
Port* p = new Port(new Method<SubtypeComponent>(sc, &SubtypeComponent::test_method2));

还要看看这些问题:
C++ member function pointers in class and subclass
Method pointer casting

答案 3 :(得分:0)

您可以使用模板适配器,因为指向成员的指针可以这样投射:

struct Base {
  bool foo(int);
};

struct Child : Base {
  bool foo(int);
};

// real functional type
using Function = std::function<bool(Base*,int)>;
// adapter template for children classes member functions
template <class T>
using MemberFunction = (T::*)(int);

// real user function
void user(Function function);
// adapter template for children classes member functions
template <class T>
void user(MemberFunction<T> function) {
  // pointers-to-members can be casted
  user(static_cast<MemberFunction<Base>>(function));
}