如何从父类调用子方法

时间:2018-12-15 15:10:57

标签: c++ c++11

我做了一个小例子,试图用我那可怜的英语向你解释我想做什么:)。

我有一个班机是我的引擎。这是我的几个孩子的父母班。

这是父类:

#include <string>
#include <iostream>
#include <vector>

template <typename Type>
class A
{
    public:
        A(std::string const &str)
        : m_str(str)
        {
        }

        void run(void) const {
            unsigned int    i;

            for(i = 0; ACTIONS[i].f != nullptr; i++) {
                if(m_str == ACTIONS[i].key) {
                    return ((*(this).*ACTIONS[i].f)(m_str));
                }
            }
        }
    protected:
        typedef struct s_action {
            std::string key;
            void (Type::*f)(std::string const &);
        } t_action;
        static t_action const ACTIONS[];
        std::string m_str;
};


class B : public A<B>
{
    public:
        B(std::string const &str);
    protected:
        static t_action const ACTIONS[];
        void error(std::string const &str);
        void success(std::string const &str);
};

我想在该父类A :: run中使用成员函数的表指针调用子方法,如上所示

此代码无法编译。

我知道不可能有一个静态变量virtual,但是 正是我需要为A :: ACTIONS做的。我绝对需要将B :: ACTIONS初始化为A :: run作品。

首先有可能吗?您有这种情况的一个小例子吗?

这是我的小代码的结尾:

#include "Class.hpp"

B::t_action const B::ACTIONS[] = {
    {"ERROR", &B::error},
    {"SUCCESS", &B::success},
    {"", nullptr}
};

B::B(std::string const &str)
: A<B>(str)
{
}

void B::error(std::string const &str) {
    std::cerr << str << std::endl;
}

void B::success(std::string const &str) {
    std::cout << str <<std::endl;
}

主要:

#include "Class.hpp"

int main() {
    B b("SUCCESS");
    b.run();
    return (0);
}

我没有尝试,通常此代码应在标准输出上显示成功

谢谢您的帮助

3 个答案:

答案 0 :(得分:2)

    void run(void) const
    {
        unsigned int    i;

        for(i = 0; ACTIONS[i].f != nullptr; i++)
            if (m_str == ACTIONS[i].key)
                return ((*(this).*ACTIONS[i].f)(m_str));
    }

无法编译有多种原因。不是一个原因,但是有几个原因。整个调度机制必须完全重新设计。

第一件事就是这是

void run(void) const

一种const类方法。

有问题的方法指针是:

void (Type::*f)(std::string const &);

方法指针不是const,而是可变的。从现有的const类方法中,您只能调用其他const方法。您不能直接或间接地从const类方法中调用非const方法。

因此,首要任务是将其更改为

void (Type::*f)(std::string const &) const;

这也意味着子类error()success()中的所有方法也必须也是const类方法。

如果有必要将此分发机制与非const方法一起使用,则run()方法不能是const类方法本身。但这不是唯一的问题,因此我将继续使用const方法。

return ((*(this).*ACTIONS[i].f)(m_str));

这里的thisA<Type>。这是该类的方法。这就是this在这里。

方法指针f是指向Type而不是A<Type>的方法的指针。 TypeA<Type>的子类,您不能将指针或对基类的引用转换为指针或对子类的引用,而只能将指针转换为{{1 }},并在A继承自B时转换为指向B的指针。 C ++无法以这种方式工作。

解决方案很简单,只需要进行一些小调整。这个A应该引用一个run(),并通过传入的引用来调用该方法,然后替换抽象const Type &的方法来调用它,并将run()作为参数传递:

*this

然后,每个继承此模板的子类仅需要实现一个简单的外观:

public:
    virtual void run()=0;

protected:

    void run_me(const Type &me) const
        {
            unsigned int    i;

            for(i = 0; ACTIONS[i].f != nullptr; i++)
                if (m_str == ACTIONS[i].key)
                    return ((me.*ACTIONS[i].f)(m_str));
        }

编辑:这解决了编译错误,但是还需要其他工作来处理不能覆盖静态类成员的事实。该解决方案也非常简单:还可以利用虚拟类方法来实现此目的。

从模板基类中删除class B : public A<B> { public: void run() const override { run_me(*this); } 的声明,并将其替换为抽象函数:

ACTIONS

并在virtual const t_action *get_actions() const=0; 中使用它:

run_me()

const t_action *ACTIONS=this->get_actions(); 的其余部分保持不变,然后在子类中实现run_me()

get_actions()

几乎所有其他内容都保持不变。

答案 1 :(得分:1)

问题在于A将始终使用自己定义的一组操作,而不是B的操作。

您根本不需要创建A,因为您想使用B方法和方法列表。

假设您首先创建一个run调用函数:

template<typename T>
void run(T* obj, const std::string method)
{
    const auto& available_methods = obj->get_methods();
    auto iter = available_methods.find(method);
    if(iter == available_methods.end())
    {
        // Handle this case
    }
    std::invoke(iter->second, obj); //C++17, or (obj->*(iter->second))();
}

现在,对于课程B,您需要一些非常简单的内容:

class B
{
public:
    typedef std::unordered_map<std::string, void(B::*)()> MethodMap;

    void foo();

    static MethodMap& get_methods()
    {
        static MethodMap map{{"foo", &B::foo}};
        return map;
    }
};

在静态函数中使用get_methods()填充地图,然后通过以下方式调用运行:

int main()
{
    B b;

    run(&b, "foo");
}

答案 2 :(得分:0)

如果您要使用CRTP和IMO,则需要先通过Google搜索CRTP。 顺便说一下,这是一个快速直接回答2你的q:

template<typename crtp_child>
class crtp_base{
    using crtp_target=crtp_child;
    auto crtp_this(){
        return static_cast<crtp_target*>(this);
    };

    auto crtp_this() const {
        return static_cast<crtp_target const*>(this);
    };
public:
    void run(){
        auto range=crtp_this()->actions.equal_range(m_str);
        for(auto entry:range)
            (crtp_this()->*(entry.second))(m_str);
    };
protected:
    crtp_base(std::string str):
        m_str(str)
    {};
    std::string m_str;
    //...
};

struct crtp_user:
    crtp_base<crtp_user>
{
    using crtp_base::crtp_base;//ctor fwding
protected:
    friend class crtp_base<crtp_user>;
    std::unordered_multimap<std::string, void (crtp_user::*)(std::string)> actions;
    //...
};