来自QMap的函数指针

时间:2018-04-18 22:01:18

标签: c++ qt function-pointers qmap

我尝试按照此示例在我的QT项目中实施工厂方法模式:https://www.codeproject.com/Articles/363338/Factory-Pattern-in-Cplusplus

#include <QCoreApplication>
#include <QDebug>

class IAnimal
{
public:
    virtual int GetNumberOfLegs() const = 0;
    virtual void Speak() = 0;
    virtual void Free() = 0;
};
typedef IAnimal* (__stdcall *CreateAnimalFn)(void);

// IAnimal implementations
class Cat : public IAnimal
{
public:
    int GetNumberOfLegs() const { return 4; }
    void Speak() { qDebug() << "Meow" << endl; }
    void Free() { delete this; }

    static IAnimal * __stdcall Create() { return new Cat(); }
};

class Dog : public IAnimal
{
public:
    int GetNumberOfLegs() const { return 4; }
    void Speak() { qDebug() << "Woof" << endl; }
    void Free() { delete this; }

    static IAnimal * __stdcall Create() { return new Dog(); }
};

工厂类:

// Factory for creating instances of IAnimal
class AnimalFactory
{
private:
    AnimalFactory();
    AnimalFactory(const AnimalFactory &) { }
    AnimalFactory &operator=(const AnimalFactory &) { return *this; }

    typedef QMap<QString,CreateAnimalFn> FactoryMap;
    FactoryMap m_FactoryMap;
public:
    ~AnimalFactory() { m_FactoryMap.clear(); }

    static AnimalFactory *Get()
    {
        static AnimalFactory instance;
        return &instance;
    }

    void Register(const QString &animalName, CreateAnimalFn pfnCreate);
    IAnimal *CreateAnimal(const QString &animalName);
};

AnimalFactory::AnimalFactory()
{
    Register("Cat", &Cat::Create);
    Register("Dog", &Dog::Create);
}

void AnimalFactory::Register(const QString &animalName, CreateAnimalFn pfnCreate)
{
    m_FactoryMap[animalName] = pfnCreate;
}

IAnimal *AnimalFactory::CreateAnimal(const QString &animalName)
{
    FactoryMap::iterator it = m_FactoryMap.find(animalName);
    if( it != m_FactoryMap.end() )
    return it.value();
    return NULL;
}

但是我遇到了这样的错误:

cannot convert 'IAnimal* (__attribute__((__stdcall__)) *)()' to 'IAnimal*' in return
         return it.value();

只有现有的anwser(Insert function pointer into QMap (Qt))建议将Create()函数设置为静态,这似乎没有帮助。

我将非常感谢任何建议。

1 个答案:

答案 0 :(得分:0)

这有点复杂。您正在编写C ++,因此您不应该复制Java。 C ++在这里更具表现力。

  1. 您不需要显式的Create / Free方法 - 编译器可以自动为您生成它们。
  2. 你肯定需要一个虚拟析构函数,否则界面将毫无用处。您打算从中派生的任何类都必须具有虚拟析构函数,此规则中几乎没有专门的例外。
  3. 虚拟方法的所有实现都应该声明为override,包括析构函数,但不是virtual,因为它违反了DRY。
  4. 这些类可以带有它们的名称,这样工厂就可以通过了解它们的类型来注册它们。这是工厂的可选行为。
  5. #include <QtCore>
    
    class IAnimal {
    public:
        virtual int GetNumberOfLegs() const = 0;
        virtual QString Speaks() = 0;
        virtual ~IAnimal() {}
    };
    
    class Cat : public IAnimal {
    public:
        int GetNumberOfLegs() const override { return 4; }
        QString Speaks() override { return QStringLiteral("Meow"); }
        static auto className() { return "Cat"; }
    };
    
    class Dog : public IAnimal {
    public:
        int GetNumberOfLegs() const override { return 4; }
        QString Speaks() override { return QStringLiteral("Woof"); }
        static auto className() { return "Dog"; }
    };
    

    现在我们可以拥有一个通用工厂。请注意,所有理智的C ++容器类型都可以管理其数据。您不需要在销毁时明确清除它们。我们正在利用C ++ 11。 Register方法只接受从Interface派生的类型,并且该方法使用lambda表达式自动生成构造函数。

    应通过在main()中实例化来明确控制实例的生命周期。

    #include <type_traits>
    #include <typeindex>
    #include <map>
    
    template <class Interface> class Factory {
      template <class C, class T = void> struct enable_if_I :
        std::enable_if<std::is_base_of<Interface, C>::value, T> {};
      using create_fn = Interface* (*)();
      std::map<QByteArray, create_fn, std::less<>> m_creators;
      std::map<std::type_index, QByteArray> m_names;
      static Factory *&instance_ref() { // assume no inline static yet
        static Factory *m_instance;
        return m_instance;
      }
      Factory(const Factory &) = delete;
      Factory &operator=(const Factory &) = delete;
    public:
      Factory() {
        Q_ASSERT(!instance());
        instance_ref() = this;
      }
      virtual ~Factory() { instance_ref() = {}; }
    

    通常,注册需要派生类的类型和名称。这无关于该类是否有className()成员的预设。工厂存储工厂功能和名称。这样可以在不使用className作为接口的虚拟方法的情况下查找名称。

      template <class T> typename enable_if_I<T>::type Register(const QByteArray &name) {
        m_creators[name] = +[]()->Interface* { return new T(); };
        m_names[{typeid(T)}] = name;
      }
    

    当知道类名时,我们可以利用它们来注册一个或多个类,只给出它们的类型。

      template <class T1> typename enable_if_I<T1>::type Register() {
        this->Register<T1>(T1::className());
      }
      template <class T1, class T2, class...T> typename enable_if_I<T1>::type Register() {
        this->Register<T1>(T1::className());
        this->Register<T2, T...>();
      }
    

    实例创建方法经过优化,不会复制给定的名称,无论格式如何。这就是我们将std::map<K, V, std::less<>>地图与透明比较器一起使用的原因。 QByteArray提供了operator<,它在右侧采用了各种类型,为了利用它,密钥的类型(此处为:name)必须到达比较器。

      template <typename T> static Interface *CreateA(T &&t) {
          return instance() ? instance()->Create(std::forward<T>(t)) : nullptr;
      }
      Interface *Create(QLatin1String name) const { return Create(name.data()); }
      template <typename T> Interface *Create(T &&name) const;
      static const QByteArray &NameOfA(const Interface * obj);
      const QByteArray &NameOf(const Interface *) const;
      static Factory *instance() { return instance_ref(); }
    };
    
    template <class Interface>
    template <typename T> Interface *Factory<Interface>::Create(T &&name) const {
      auto it = m_creators.find(name);
      return (it != m_creators.end()) ? it->second() : nullptr;
    }
    namespace detail { 
    const QByteArray & null() { static const QByteArray n; return n; }
    }
    template <class Interface>
    const QByteArray &Factory<Interface>::NameOfA(const Interface *obj) {
      return instance() ? instance()->NameOf(obj) : detail::null();
    }
    template <class Interface>
    const QByteArray &Factory<Interface>::NameOf(const Interface *obj) const {
      auto it = m_names.find(typeid(*obj));
      return (it != m_names.end()) ? it->second : detail::null();
    }
    

    泛型工厂接受接口和具体类型,并在构造函数中将它们全部注册。这使得建造工厂变得简单。

    template <class Interface, class ...Types>
    class GenericFactory : public Factory<Interface> {
    public:
      GenericFactory() {
        this->template Register<Types...>();
      }
    };
    
    using AnimalFactory = GenericFactory<IAnimal, Cat, Dog>;
    

    使用示例,带有断言以指示所需行为。请注意,要销毁对象,只需要delete个实例。编译器将生成调用。

    int main() {
      Q_ASSERT(!AnimalFactory::instance());
      {
        AnimalFactory animals;
        Q_ASSERT(AnimalFactory::instance());
        auto *dog1 = AnimalFactory::CreateA("Dog");
        Q_ASSERT(dynamic_cast<Dog*>(dog1));
        Q_ASSERT(AnimalFactory::NameOfA(dog1) == Dog::className());
        Q_ASSERT(dog1->Speaks() == QStringLiteral("Woof"));
        auto *dog2 = AnimalFactory::CreateA(QLatin1String("Dog"));
        Q_ASSERT(dynamic_cast<Dog*>(dog2));
        auto *cat = AnimalFactory::CreateA("Cat");
        Q_ASSERT(dynamic_cast<Cat*>(cat));
        Q_ASSERT(cat->Speaks() == QStringLiteral("Meow"));
        Q_ASSERT(AnimalFactory::NameOfA(cat) == Cat::className());
        delete cat;
        delete dog2;
        delete dog1;
      }
      Q_ASSERT(!AnimalFactory::instance());
    }